Улучшение безопасности и обработки ошибок. Этап 1. #8
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
password_hash :: binary(),
|
password_hash :: binary(),
|
||||||
role :: user | bot,
|
role :: user | bot,
|
||||||
status :: active | frozen | deleted,
|
status :: active | frozen | deleted,
|
||||||
|
reason :: binary() | undefined,
|
||||||
created_at :: calendar:datetime(),
|
created_at :: calendar:datetime(),
|
||||||
updated_at :: calendar:datetime()
|
updated_at :: calendar:datetime()
|
||||||
}).
|
}).
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
rating_avg :: float(),
|
rating_avg :: float(),
|
||||||
rating_count :: non_neg_integer(),
|
rating_count :: non_neg_integer(),
|
||||||
status :: active | frozen | deleted,
|
status :: active | frozen | deleted,
|
||||||
|
reason :: binary() | undefined,
|
||||||
created_at :: calendar:datetime(),
|
created_at :: calendar:datetime(),
|
||||||
updated_at :: calendar:datetime()
|
updated_at :: calendar:datetime()
|
||||||
}).
|
}).
|
||||||
@@ -85,6 +87,7 @@
|
|||||||
capacity :: integer() | undefined,
|
capacity :: integer() | undefined,
|
||||||
online_link :: binary() | undefined,
|
online_link :: binary() | undefined,
|
||||||
status :: active | cancelled | completed,
|
status :: active | cancelled | completed,
|
||||||
|
reason :: binary() | undefined,
|
||||||
rating_avg :: float(),
|
rating_avg :: float(),
|
||||||
rating_count :: non_neg_integer(),
|
rating_count :: non_neg_integer(),
|
||||||
created_at :: calendar:datetime(),
|
created_at :: calendar:datetime(),
|
||||||
@@ -118,6 +121,7 @@
|
|||||||
rating :: 1..5,
|
rating :: 1..5,
|
||||||
comment :: binary(),
|
comment :: binary(),
|
||||||
status :: visible | hidden | deleted,
|
status :: visible | hidden | deleted,
|
||||||
|
reason :: binary() | undefined,
|
||||||
created_at :: calendar:datetime(),
|
created_at :: calendar:datetime(),
|
||||||
updated_at :: calendar:datetime()
|
updated_at :: calendar:datetime()
|
||||||
}).
|
}).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-module(core_admin_audit).
|
-module(core_admin_audit).
|
||||||
-include("records.hrl").
|
-include("records.hrl").
|
||||||
-export([log/7, list/0, list/1]).
|
-export([log/7, log/8, list/0, list/1]). % ← добавили log/8
|
||||||
-export([count_actions_by_admin/2]).
|
-export([count_actions_by_admin/2]).
|
||||||
|
|
||||||
log(AdminId, Email, Role, Action, EntityType, EntityId, Ip) ->
|
log(AdminId, Email, Role, Action, EntityType, EntityId, Ip) ->
|
||||||
@@ -25,33 +25,28 @@ log(AdminId, Email, Role, Action, EntityType, EntityId, Ip, Reason) ->
|
|||||||
list() ->
|
list() ->
|
||||||
mnesia:dirty_match_object(#admin_audit{_ = '_'}).
|
mnesia:dirty_match_object(#admin_audit{_ = '_'}).
|
||||||
|
|
||||||
%% Фильтрация по параметрам (простая версия)
|
|
||||||
list(Filters) ->
|
list(Filters) ->
|
||||||
All = list(), % все записи
|
All = list(),
|
||||||
lists:filter(fun(E) ->
|
lists:filter(fun(E) ->
|
||||||
% Фильтр по admin_id
|
|
||||||
case proplists:get_value(admin_id, Filters) of
|
case proplists:get_value(admin_id, Filters) of
|
||||||
undefined -> true;
|
undefined -> true;
|
||||||
Id -> E#admin_audit.admin_id =:= Id
|
Id -> E#admin_audit.admin_id =:= Id
|
||||||
end
|
end
|
||||||
andalso
|
andalso
|
||||||
% Фильтр по action
|
case proplists:get_value(action, Filters) of
|
||||||
case proplists:get_value(action, Filters) of
|
undefined -> true;
|
||||||
undefined -> true;
|
Act -> E#admin_audit.action =:= Act
|
||||||
Act -> E#admin_audit.action =:= Act
|
end
|
||||||
end
|
|
||||||
andalso
|
andalso
|
||||||
% Фильтр по дате с
|
case proplists:get_value(date_from, Filters) of
|
||||||
case proplists:get_value(date_from, Filters) of
|
undefined -> true;
|
||||||
undefined -> true;
|
From -> E#admin_audit.timestamp >= From
|
||||||
From -> E#admin_audit.timestamp >= From
|
end
|
||||||
end
|
|
||||||
andalso
|
andalso
|
||||||
% Фильтр по дате по
|
case proplists:get_value(date_to, Filters) of
|
||||||
case proplists:get_value(date_to, Filters) of
|
undefined -> true;
|
||||||
undefined -> true;
|
To -> E#admin_audit.timestamp =< To
|
||||||
To -> E#admin_audit.timestamp =< To
|
end
|
||||||
end
|
|
||||||
end, All).
|
end, All).
|
||||||
|
|
||||||
count_actions_by_admin(AdminId, Action) ->
|
count_actions_by_admin(AdminId, Action) ->
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
-module(core_calendar).
|
-module(core_calendar).
|
||||||
-include("records.hrl").
|
-include("records.hrl").
|
||||||
|
|
||||||
-export([create/4, create/5, get_by_id/1, list_by_owner/1, update/2, delete/1]).
|
-export([create/4, create/5, get_by_id/1, list_by_owner/1, update/2, delete/1]).
|
||||||
-export([generate_id/0]).
|
-export([generate_id/0]).
|
||||||
-export([count_calendars/0]).
|
-export([count_calendars/0]).
|
||||||
|
-export([freeze/2, unfreeze/2]). % ← новые функции
|
||||||
|
|
||||||
%% Создание календаря
|
%% Создание календаря
|
||||||
create(OwnerId, Title, Description, Confirmation) ->
|
create(OwnerId, Title, Description, Confirmation) ->
|
||||||
@@ -22,12 +22,7 @@ create(OwnerId, Title, Description, Confirmation) ->
|
|||||||
created_at = calendar:universal_time(),
|
created_at = calendar:universal_time(),
|
||||||
updated_at = calendar:universal_time()
|
updated_at = calendar:universal_time()
|
||||||
},
|
},
|
||||||
|
F = fun() -> mnesia:write(Calendar), {ok, Calendar} end,
|
||||||
F = fun() ->
|
|
||||||
mnesia:write(Calendar),
|
|
||||||
{ok, Calendar}
|
|
||||||
end,
|
|
||||||
|
|
||||||
case mnesia:transaction(F) of
|
case mnesia:transaction(F) of
|
||||||
{atomic, Result} -> Result;
|
{atomic, Result} -> Result;
|
||||||
{aborted, Reason} -> {error, Reason}
|
{aborted, Reason} -> {error, Reason}
|
||||||
@@ -50,12 +45,7 @@ create(OwnerId, Title, Description, Confirmation, Type) ->
|
|||||||
created_at = calendar:universal_time(),
|
created_at = calendar:universal_time(),
|
||||||
updated_at = calendar:universal_time()
|
updated_at = calendar:universal_time()
|
||||||
},
|
},
|
||||||
|
F = fun() -> mnesia:write(Calendar), {ok, Calendar} end,
|
||||||
F = fun() ->
|
|
||||||
mnesia:write(Calendar),
|
|
||||||
{ok, Calendar}
|
|
||||||
end,
|
|
||||||
|
|
||||||
case mnesia:transaction(F) of
|
case mnesia:transaction(F) of
|
||||||
{atomic, Result} -> Result;
|
{atomic, Result} -> Result;
|
||||||
{aborted, Reason} -> {error, Reason}
|
{aborted, Reason} -> {error, Reason}
|
||||||
@@ -78,15 +68,13 @@ list_by_owner(OwnerId) ->
|
|||||||
update(Id, Updates) ->
|
update(Id, Updates) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
case mnesia:read(calendar, Id) of
|
case mnesia:read(calendar, Id) of
|
||||||
[] ->
|
[] -> {error, not_found};
|
||||||
{error, not_found};
|
|
||||||
[Calendar] ->
|
[Calendar] ->
|
||||||
UpdatedCalendar = apply_updates(Calendar, Updates),
|
UpdatedCalendar = apply_updates(Calendar, Updates),
|
||||||
mnesia:write(UpdatedCalendar),
|
mnesia:write(UpdatedCalendar),
|
||||||
{ok, UpdatedCalendar}
|
{ok, UpdatedCalendar}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
case mnesia:transaction(F) of
|
case mnesia:transaction(F) of
|
||||||
{atomic, Result} -> Result;
|
{atomic, Result} -> Result;
|
||||||
{aborted, Reason} -> {error, Reason}
|
{aborted, Reason} -> {error, Reason}
|
||||||
@@ -96,11 +84,20 @@ update(Id, Updates) ->
|
|||||||
delete(Id) ->
|
delete(Id) ->
|
||||||
update(Id, [{status, deleted}]).
|
update(Id, [{status, deleted}]).
|
||||||
|
|
||||||
count_calendars() -> mnesia:table_info(calendar, size).
|
count_calendars() ->
|
||||||
|
mnesia:table_info(calendar, size).
|
||||||
|
|
||||||
|
%% ── НОВЫЕ ФУНКЦИИ ──────────────────────────────────────────
|
||||||
|
freeze(Id, Reason) ->
|
||||||
|
update(Id, [{status, frozen}, {reason, Reason}]).
|
||||||
|
|
||||||
|
unfreeze(Id, Reason) ->
|
||||||
|
update(Id, [{status, active}, {reason, Reason}]).
|
||||||
|
|
||||||
%% Внутренние функции
|
%% Внутренние функции
|
||||||
generate_id() ->
|
generate_id() ->
|
||||||
base64:encode(crypto:strong_rand_bytes(16), #{mode => urlsafe, padding => false}).
|
base64:encode(crypto:strong_rand_bytes(16), #{mode => urlsafe, padding => false}).
|
||||||
|
|
||||||
apply_updates(Calendar, Updates) ->
|
apply_updates(Calendar, Updates) ->
|
||||||
Updated = lists:foldl(fun({Field, Value}, C) ->
|
Updated = lists:foldl(fun({Field, Value}, C) ->
|
||||||
set_field(Field, Value, C)
|
set_field(Field, Value, C)
|
||||||
@@ -115,4 +112,5 @@ set_field(confirmation, Value, C) -> C#calendar{confirmation = Value};
|
|||||||
set_field(status, Value, C) -> C#calendar{status = Value};
|
set_field(status, Value, C) -> C#calendar{status = Value};
|
||||||
set_field(rating_avg, Value, C) -> C#calendar{rating_avg = Value};
|
set_field(rating_avg, Value, C) -> C#calendar{rating_avg = Value};
|
||||||
set_field(rating_count, Value, C) -> C#calendar{rating_count = Value};
|
set_field(rating_count, Value, C) -> C#calendar{rating_count = Value};
|
||||||
|
set_field(reason, Value, C) -> C#calendar{reason = Value}; % ← поддержка поля reason
|
||||||
set_field(_, _, C) -> C.
|
set_field(_, _, C) -> C.
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
update/2, delete/1, materialize_occurrence/3]).
|
update/2, delete/1, materialize_occurrence/3]).
|
||||||
-export([generate_id/0]).
|
-export([generate_id/0]).
|
||||||
-export([count_events/0, count_events_by_date/2]).
|
-export([count_events/0, count_events_by_date/2]).
|
||||||
|
-export([freeze/2, unfreeze/2]).
|
||||||
|
|
||||||
%% Создание одиночного события
|
%% Создание одиночного события
|
||||||
create(CalendarId, Title, StartTime, Duration) ->
|
create(CalendarId, Title, StartTime, Duration) ->
|
||||||
@@ -197,6 +198,13 @@ apply_updates(Event, Updates) ->
|
|||||||
end, Event, Updates),
|
end, Event, Updates),
|
||||||
Updated#event{updated_at = calendar:universal_time()}.
|
Updated#event{updated_at = calendar:universal_time()}.
|
||||||
|
|
||||||
|
freeze(Id, Reason) ->
|
||||||
|
update(Id, [{status, frozen}, {reason, Reason}]).
|
||||||
|
|
||||||
|
unfreeze(Id, Reason) ->
|
||||||
|
update(Id, [{status, active}, {reason, Reason}]).
|
||||||
|
|
||||||
|
%% ── ОБНОВЛЁННАЯ set_field ──────────────────────────────────
|
||||||
set_field(title, Value, E) -> E#event{title = Value};
|
set_field(title, Value, E) -> E#event{title = Value};
|
||||||
set_field(description, Value, E) -> E#event{description = Value};
|
set_field(description, Value, E) -> E#event{description = Value};
|
||||||
set_field(start_time, Value, E) -> E#event{start_time = Value};
|
set_field(start_time, Value, E) -> E#event{start_time = Value};
|
||||||
@@ -207,6 +215,7 @@ set_field(tags, Value, E) -> E#event{tags = Value};
|
|||||||
set_field(capacity, Value, E) -> E#event{capacity = Value};
|
set_field(capacity, Value, E) -> E#event{capacity = Value};
|
||||||
set_field(online_link, Value, E) -> E#event{online_link = Value};
|
set_field(online_link, Value, E) -> E#event{online_link = Value};
|
||||||
set_field(status, Value, E) -> E#event{status = Value};
|
set_field(status, Value, E) -> E#event{status = Value};
|
||||||
|
set_field(reason, Value, E) -> E#event{reason = Value};
|
||||||
set_field(rating_avg, Value, E) -> E#event{rating_avg = Value};
|
set_field(rating_avg, Value, E) -> E#event{rating_avg = Value};
|
||||||
set_field(rating_count, Value, E) -> E#event{rating_count = Value};
|
set_field(rating_count, Value, E) -> E#event{rating_count = Value};
|
||||||
set_field(_, _, E) -> E.
|
set_field(_, _, E) -> E.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
-include("records.hrl").
|
-include("records.hrl").
|
||||||
|
|
||||||
-export([create/5, get_by_id/1, list_by_target/2, list_by_user/1,
|
-export([create/5, get_by_id/1, list_by_target/2, list_by_user/1,
|
||||||
update/2, delete/1, hide/1, unhide/1]).
|
update/2, delete/1, hide/2, unhide/2]).
|
||||||
-export([get_average_rating/2, has_user_reviewed/3]).
|
-export([get_average_rating/2, has_user_reviewed/3]).
|
||||||
-export([generate_id/0]).
|
-export([generate_id/0]).
|
||||||
-export([count_reviews/0]).
|
-export([count_reviews/0]).
|
||||||
@@ -87,12 +87,11 @@ delete(Id) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
%% Скрытие отзыва (модерация)
|
%% Скрытие отзыва (модерация)
|
||||||
hide(Id) ->
|
hide(Id, Reason) ->
|
||||||
update(Id, [{status, hidden}]).
|
update(Id, [{status, hidden}, {reason, Reason}]).
|
||||||
|
|
||||||
%% Раскрытие отзыва
|
unhide(Id, Reason) ->
|
||||||
unhide(Id) ->
|
update(Id, [{status, visible}, {reason, Reason}]).
|
||||||
update(Id, [{status, visible}]).
|
|
||||||
|
|
||||||
%% Получение среднего рейтинга цели
|
%% Получение среднего рейтинга цели
|
||||||
get_average_rating(TargetType, TargetId) ->
|
get_average_rating(TargetType, TargetId) ->
|
||||||
@@ -129,4 +128,5 @@ apply_updates(Review, Updates) ->
|
|||||||
set_field(rating, Value, R) when Value >= 1, Value =< 5 -> R#review{rating = Value};
|
set_field(rating, Value, R) when Value >= 1, Value =< 5 -> R#review{rating = Value};
|
||||||
set_field(comment, Value, R) -> R#review{comment = Value};
|
set_field(comment, Value, R) -> R#review{comment = Value};
|
||||||
set_field(status, Value, R) when Value =:= visible; Value =:= hidden -> R#review{status = Value};
|
set_field(status, Value, R) when Value =:= visible; Value =:= hidden -> R#review{status = Value};
|
||||||
|
set_field(reason, Value, R) -> R#review{reason = Value};
|
||||||
set_field(_, _, R) -> R.
|
set_field(_, _, R) -> R.
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
-module(core_user).
|
-module(core_user).
|
||||||
-include("records.hrl").
|
-include("records.hrl").
|
||||||
|
|
||||||
-export([create/2, get_by_id/1, get_by_email/1, update/2, delete/1]).
|
-export([create/2, get_by_id/1, get_by_email/1, update/2, update_status/3, delete/1]).
|
||||||
-export([email_exists/1]).
|
-export([email_exists/1]).
|
||||||
-export([generate_id/0]).
|
-export([generate_id/0]).
|
||||||
-export([list_users/0]).
|
-export([list_users/0]).
|
||||||
-export([block/1, unblock/1]).
|
-export([block/2, unblock/2]).
|
||||||
-export([count_users/0, count_users_by_date/2]).
|
-export([count_users/0, count_users_by_date/2]).
|
||||||
|
|
||||||
%% Создание пользователя
|
%% Создание пользователя
|
||||||
@@ -85,6 +85,15 @@ update(Id, Updates) ->
|
|||||||
{aborted, Reason} -> {error, Reason}
|
{aborted, Reason} -> {error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
update_status(Id, Status, Reason) ->
|
||||||
|
case get_by_id(Id) of
|
||||||
|
{ok, User} ->
|
||||||
|
Updated = User#user{status = Status, reason = Reason, updated_at = calendar:universal_time()},
|
||||||
|
mnesia:dirty_write(Updated),
|
||||||
|
{ok, Updated};
|
||||||
|
Error -> Error
|
||||||
|
end.
|
||||||
|
|
||||||
%% Удаление пользователя (soft delete)
|
%% Удаление пользователя (soft delete)
|
||||||
delete(Id) ->
|
delete(Id) ->
|
||||||
update(Id, [{status, deleted}]).
|
update(Id, [{status, deleted}]).
|
||||||
@@ -94,30 +103,41 @@ list_users() ->
|
|||||||
ActiveUsers = [U || U <- Users, U#user.status =/= deleted],
|
ActiveUsers = [U || U <- Users, U#user.status =/= deleted],
|
||||||
{ok, [user_to_map(U) || U <- ActiveUsers]}.
|
{ok, [user_to_map(U) || U <- ActiveUsers]}.
|
||||||
|
|
||||||
|
user_to_map(User) when is_map(User) ->
|
||||||
|
#{
|
||||||
|
id => maps:get(id, User),
|
||||||
|
email => maps:get(email, User),
|
||||||
|
role => maps:get(role, User, <<"user">>),
|
||||||
|
status => maps:get(status, User, <<"active">>),
|
||||||
|
reason => maps:get(reason, User, undefined),
|
||||||
|
created_at => maps:get(created_at, User),
|
||||||
|
updated_at => maps:get(updated_at, User)
|
||||||
|
};
|
||||||
|
|
||||||
user_to_map(User) ->
|
user_to_map(User) ->
|
||||||
#{
|
#{
|
||||||
id => User#user.id,
|
id => User#user.id,
|
||||||
email => User#user.email,
|
email => User#user.email,
|
||||||
password_hash => User#user.password_hash,
|
role => atom_to_binary(User#user.role, utf8),
|
||||||
role => User#user.role,
|
status => atom_to_binary(User#user.status, utf8),
|
||||||
status => User#user.status,
|
reason => User#user.reason,
|
||||||
created_at => User#user.created_at,
|
created_at => User#user.created_at,
|
||||||
updated_at => User#user.updated_at
|
updated_at => User#user.updated_at
|
||||||
}.
|
}.
|
||||||
|
|
||||||
block(Id) ->
|
block(Id, Reason) ->
|
||||||
case get_by_id(Id) of
|
case get_by_id(Id) of
|
||||||
{ok, User} ->
|
{ok, User} ->
|
||||||
Updated = User#user{status = blocked, updated_at = calendar:universal_time()},
|
Updated = User#user{status = blocked, reason = Reason, updated_at = calendar:universal_time()},
|
||||||
mnesia:dirty_write(Updated),
|
mnesia:dirty_write(Updated),
|
||||||
{ok, Updated};
|
{ok, Updated};
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
unblock(Id) ->
|
unblock(Id, Reason) ->
|
||||||
case get_by_id(Id) of
|
case get_by_id(Id) of
|
||||||
{ok, User} ->
|
{ok, User} ->
|
||||||
Updated = User#user{status = active, updated_at = calendar:universal_time()},
|
Updated = User#user{status = active, reason = Reason, updated_at = calendar:universal_time()},
|
||||||
mnesia:dirty_write(Updated),
|
mnesia:dirty_write(Updated),
|
||||||
{ok, Updated};
|
{ok, Updated};
|
||||||
Error -> Error
|
Error -> Error
|
||||||
|
|||||||
@@ -13,16 +13,17 @@ init(Req, _Opts) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
moderate(Req) ->
|
moderate(Req) ->
|
||||||
case auth_admin(Req) of
|
case authenticate_and_check_admin(Req) of
|
||||||
{ok, _AdminId, Req1} ->
|
{ok, AdminId, Req1} ->
|
||||||
TargetType = cowboy_req:binding(target_type, Req1),
|
TargetType = cowboy_req:binding(target_type, Req1),
|
||||||
TargetId = cowboy_req:binding(id, Req1),
|
TargetId = cowboy_req:binding(id, Req1),
|
||||||
case lists:member(TargetType, ?VALID_TARGETS) of
|
case lists:member(TargetType, ?VALID_TARGETS) of
|
||||||
true ->
|
true ->
|
||||||
{ok, Body, Req2} = cowboy_req:read_body(Req1),
|
{ok, Body, Req2} = cowboy_req:read_body(Req1),
|
||||||
try jsx:decode(Body, [return_maps]) of
|
try jsx:decode(Body, [return_maps]) of
|
||||||
#{<<"action">> := Action} ->
|
#{<<"action">> := Action} = BodyMap ->
|
||||||
apply_moderation(TargetType, TargetId, Action, Req2);
|
Reason = maps:get(<<"reason">>, BodyMap, <<"">>),
|
||||||
|
apply_moderation(TargetType, TargetId, Action, Reason, Req2, AdminId);
|
||||||
_ ->
|
_ ->
|
||||||
send_error(Req2, 400, <<"Missing 'action' field">>)
|
send_error(Req2, 400, <<"Missing 'action' field">>)
|
||||||
catch
|
catch
|
||||||
@@ -35,68 +36,95 @@ moderate(Req) ->
|
|||||||
send_error(Req1, Code, Message)
|
send_error(Req1, Code, Message)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
apply_moderation(<<"calendar">>, Id, Action, Req) ->
|
apply_moderation(<<"calendar">>, Id, Action, Reason, Req, AdminId) ->
|
||||||
handle_calendar(Id, Action, Req);
|
handle_calendar(Id, Action, Reason, Req, AdminId);
|
||||||
apply_moderation(<<"event">>, Id, Action, Req) ->
|
apply_moderation(<<"event">>, Id, Action, Reason, Req, AdminId) ->
|
||||||
handle_event(Id, Action, Req);
|
handle_event(Id, Action, Reason, Req, AdminId);
|
||||||
apply_moderation(<<"review">>, Id, Action, Req) ->
|
apply_moderation(<<"review">>, Id, Action, Reason, Req, AdminId) ->
|
||||||
handle_review(Id, Action, Req);
|
handle_review(Id, Action, Reason, Req, AdminId);
|
||||||
apply_moderation(<<"user">>, Id, Action, Req) ->
|
apply_moderation(<<"user">>, Id, Action, Reason, Req, AdminId) ->
|
||||||
handle_user(Id, Action, Req).
|
handle_user(Id, Action, Reason, Req, AdminId).
|
||||||
|
|
||||||
handle_calendar(Id, <<"freeze">>, Req) ->
|
handle_calendar(Id, <<"freeze">>, Reason, Req, AdminId) ->
|
||||||
case core_calendar:freeze(Id) of
|
case core_calendar:freeze(Id, Reason) of
|
||||||
{ok, Calendar} -> send_json(Req, 200, calendar_to_json(Calendar));
|
{ok, Calendar} ->
|
||||||
|
log_audit(AdminId, <<"freeze_calendar">>, <<"calendar">>, Id, Reason),
|
||||||
|
send_json(Req, 200, calendar_to_json(Calendar));
|
||||||
{error, not_found} -> send_error(Req, 404, <<"Calendar not found">>)
|
{error, not_found} -> send_error(Req, 404, <<"Calendar not found">>)
|
||||||
end;
|
end;
|
||||||
handle_calendar(Id, <<"unfreeze">>, Req) ->
|
handle_calendar(Id, <<"unfreeze">>, Reason, Req, AdminId) ->
|
||||||
case core_calendar:unfreeze(Id) of
|
case core_calendar:unfreeze(Id, Reason) of
|
||||||
{ok, Calendar} -> send_json(Req, 200, calendar_to_json(Calendar));
|
{ok, Calendar} ->
|
||||||
|
log_audit(AdminId, <<"unfreeze_calendar">>, <<"calendar">>, Id, Reason),
|
||||||
|
send_json(Req, 200, calendar_to_json(Calendar));
|
||||||
{error, not_found} -> send_error(Req, 404, <<"Calendar not found">>)
|
{error, not_found} -> send_error(Req, 404, <<"Calendar not found">>)
|
||||||
end;
|
end;
|
||||||
handle_calendar(_Id, _Action, Req) ->
|
handle_calendar(_Id, _Action, _Reason, Req, _AdminId) ->
|
||||||
send_error(Req, 400, <<"Invalid action for calendar">>).
|
send_error(Req, 400, <<"Invalid action for calendar">>).
|
||||||
|
|
||||||
handle_event(Id, <<"freeze">>, Req) ->
|
handle_event(Id, <<"freeze">>, Reason, Req, AdminId) ->
|
||||||
case core_event:freeze(Id) of
|
case core_event:freeze(Id, Reason) of
|
||||||
{ok, Event} -> send_json(Req, 200, event_to_json(Event));
|
{ok, Event} ->
|
||||||
|
log_audit(AdminId, <<"freeze_event">>, <<"event">>, Id, Reason),
|
||||||
|
send_json(Req, 200, event_to_json(Event));
|
||||||
{error, not_found} -> send_error(Req, 404, <<"Event not found">>)
|
{error, not_found} -> send_error(Req, 404, <<"Event not found">>)
|
||||||
end;
|
end;
|
||||||
handle_event(Id, <<"unfreeze">>, Req) ->
|
handle_event(Id, <<"unfreeze">>, Reason, Req, AdminId) ->
|
||||||
case core_event:unfreeze(Id) of
|
case core_event:unfreeze(Id, Reason) of
|
||||||
{ok, Event} -> send_json(Req, 200, event_to_json(Event));
|
{ok, Event} ->
|
||||||
|
log_audit(AdminId, <<"unfreeze_event">>, <<"event">>, Id, Reason),
|
||||||
|
send_json(Req, 200, event_to_json(Event));
|
||||||
{error, not_found} -> send_error(Req, 404, <<"Event not found">>)
|
{error, not_found} -> send_error(Req, 404, <<"Event not found">>)
|
||||||
end;
|
end;
|
||||||
handle_event(_Id, _Action, Req) ->
|
handle_event(_Id, _Action, _Reason, Req, _AdminId) ->
|
||||||
send_error(Req, 400, <<"Invalid action for event">>).
|
send_error(Req, 400, <<"Invalid action for event">>).
|
||||||
|
|
||||||
handle_review(Id, <<"hide">>, Req) ->
|
handle_review(Id, <<"hide">>, Reason, Req, AdminId) ->
|
||||||
case core_review:hide(Id) of
|
case core_review:hide(Id, Reason) of
|
||||||
{ok, Review} -> send_json(Req, 200, review_to_json(Review));
|
{ok, Review} ->
|
||||||
|
log_audit(AdminId, <<"hide_review">>, <<"review">>, Id, Reason),
|
||||||
|
send_json(Req, 200, review_to_json(Review));
|
||||||
{error, not_found} -> send_error(Req, 404, <<"Review not found">>)
|
{error, not_found} -> send_error(Req, 404, <<"Review not found">>)
|
||||||
end;
|
end;
|
||||||
handle_review(Id, <<"show">>, Req) ->
|
handle_review(Id, <<"unhide">>, Reason, Req, AdminId) ->
|
||||||
case core_review:show(Id) of
|
case core_review:unhide(Id, Reason) of
|
||||||
{ok, Review} -> send_json(Req, 200, review_to_json(Review));
|
{ok, Review} ->
|
||||||
|
log_audit(AdminId, <<"unhide_review">>, <<"review">>, Id, Reason),
|
||||||
|
send_json(Req, 200, review_to_json(Review));
|
||||||
{error, not_found} -> send_error(Req, 404, <<"Review not found">>)
|
{error, not_found} -> send_error(Req, 404, <<"Review not found">>)
|
||||||
end;
|
end;
|
||||||
handle_review(_Id, _Action, Req) ->
|
handle_review(_Id, _Action, _Reason, Req, _AdminId) ->
|
||||||
send_error(Req, 400, <<"Invalid action for review">>).
|
send_error(Req, 400, <<"Invalid action for review">>).
|
||||||
|
|
||||||
handle_user(Id, <<"block">>, Req) ->
|
handle_user(Id, <<"block">>, Reason, Req, AdminId) ->
|
||||||
case core_user:block(Id) of
|
case core_user:block(Id, Reason) of
|
||||||
{ok, User} -> send_json(Req, 200, user_to_json(User));
|
{ok, User} ->
|
||||||
|
log_audit(AdminId, <<"block_user">>, <<"user">>, Id, Reason),
|
||||||
|
send_json(Req, 200, user_to_json(User));
|
||||||
{error, not_found} -> send_error(Req, 404, <<"User not found">>)
|
{error, not_found} -> send_error(Req, 404, <<"User not found">>)
|
||||||
end;
|
end;
|
||||||
handle_user(Id, <<"unblock">>, Req) ->
|
handle_user(Id, <<"unblock">>, Reason, Req, AdminId) ->
|
||||||
case core_user:unblock(Id) of
|
case core_user:unblock(Id, Reason) of
|
||||||
{ok, User} -> send_json(Req, 200, user_to_json(User));
|
{ok, User} ->
|
||||||
|
log_audit(AdminId, <<"unblock_user">>, <<"user">>, Id, Reason),
|
||||||
|
send_json(Req, 200, user_to_json(User));
|
||||||
{error, not_found} -> send_error(Req, 404, <<"User not found">>)
|
{error, not_found} -> send_error(Req, 404, <<"User not found">>)
|
||||||
end;
|
end;
|
||||||
handle_user(_Id, _Action, Req) ->
|
handle_user(_Id, _Action, _Reason, Req, _AdminId) ->
|
||||||
send_error(Req, 400, <<"Invalid action for user">>).
|
send_error(Req, 400, <<"Invalid action for user">>).
|
||||||
|
|
||||||
auth_admin(Req) ->
|
%% ── АУДИТ ──────────────────────────────────────────────────
|
||||||
|
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.
|
||||||
|
|
||||||
|
%% ── ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ────────────────────────────────
|
||||||
|
authenticate_and_check_admin(Req) ->
|
||||||
case handler_auth:authenticate(Req) of
|
case handler_auth:authenticate(Req) of
|
||||||
{ok, AdminId, Req1} ->
|
{ok, AdminId, Req1} ->
|
||||||
case admin_utils:is_admin(AdminId) of
|
case admin_utils:is_admin(AdminId) of
|
||||||
@@ -107,31 +135,37 @@ auth_admin(Req) ->
|
|||||||
{error, Code, Message, Req1}
|
{error, Code, Message, Req1}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
client_ip() -> <<"127.0.0.1">>.
|
||||||
|
|
||||||
calendar_to_json(C) ->
|
calendar_to_json(C) ->
|
||||||
#{
|
#{
|
||||||
id => C#calendar.id,
|
id => C#calendar.id,
|
||||||
title => C#calendar.title,
|
title => C#calendar.title,
|
||||||
status => atom_to_binary(C#calendar.status, utf8)
|
status => atom_to_binary(C#calendar.status, utf8),
|
||||||
|
reason => C#calendar.reason
|
||||||
}.
|
}.
|
||||||
|
|
||||||
event_to_json(E) ->
|
event_to_json(E) ->
|
||||||
#{
|
#{
|
||||||
id => E#event.id,
|
id => E#event.id,
|
||||||
title => E#event.title,
|
title => E#event.title,
|
||||||
status => atom_to_binary(E#event.status, utf8)
|
status => atom_to_binary(E#event.status, utf8),
|
||||||
|
reason => E#event.reason
|
||||||
}.
|
}.
|
||||||
|
|
||||||
review_to_json(R) ->
|
review_to_json(R) ->
|
||||||
#{
|
#{
|
||||||
id => R#review.id,
|
id => R#review.id,
|
||||||
status => atom_to_binary(R#review.status, utf8)
|
status => atom_to_binary(R#review.status, utf8),
|
||||||
|
reason => R#review.reason
|
||||||
}.
|
}.
|
||||||
|
|
||||||
user_to_json(U) ->
|
user_to_json(U) ->
|
||||||
#{
|
#{
|
||||||
id => U#user.id,
|
id => U#user.id,
|
||||||
email => U#user.email,
|
email => U#user.email,
|
||||||
status => atom_to_binary(U#user.status, utf8)
|
status => atom_to_binary(U#user.status, utf8),
|
||||||
|
reason => U#user.reason
|
||||||
}.
|
}.
|
||||||
|
|
||||||
send_json(Req, Status, Data) ->
|
send_json(Req, Status, Data) ->
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ init(Req, _Opts) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_report(Req) ->
|
get_report(Req) ->
|
||||||
case handler_auth:authenticate(Req) of
|
case auth_admin(Req) of
|
||||||
{ok, AdminId, Req1} ->
|
{ok, AdminId, Req1} ->
|
||||||
case admin_utils:is_admin(AdminId) of
|
case admin_utils:is_admin(AdminId) of
|
||||||
true ->
|
true ->
|
||||||
@@ -31,17 +31,18 @@ get_report(Req) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
update_report(Req) ->
|
update_report(Req) ->
|
||||||
case handler_auth:authenticate(Req) of
|
case auth_admin(Req) of
|
||||||
{ok, AdminId, Req1} ->
|
{ok, AdminId, Req1} ->
|
||||||
case admin_utils:is_admin(AdminId) of
|
case admin_utils:is_admin(AdminId) of
|
||||||
true ->
|
true ->
|
||||||
ReportId = cowboy_req:binding(id, Req1),
|
ReportId = cowboy_req:binding(id, Req1),
|
||||||
{ok, Body, Req2} = cowboy_req:read_body(Req1),
|
{ok, Body, Req2} = cowboy_req:read_body(Req1),
|
||||||
try jsx:decode(Body, [return_maps]) of
|
try jsx:decode(Body, [return_maps]) of
|
||||||
#{<<"status">> := NewStatus} ->
|
#{<<"status">> := NewStatus, <<"reason">> := Reason} ->
|
||||||
StatusAtom = binary_to_atom(NewStatus, utf8),
|
StatusAtom = binary_to_atom(NewStatus, utf8),
|
||||||
case core_report:update_status(ReportId, StatusAtom, AdminId) of
|
case core_report:update_status(ReportId, StatusAtom, AdminId) of
|
||||||
{ok, Report} ->
|
{ok, Report} ->
|
||||||
|
log_audit(AdminId, <<"update_report_status">>, <<"report">>, ReportId, Reason),
|
||||||
send_json(Req2, 200, report_to_json(Report));
|
send_json(Req2, 200, report_to_json(Report));
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
send_error(Req2, 404, <<"Report not found">>);
|
send_error(Req2, 404, <<"Report not found">>);
|
||||||
@@ -49,7 +50,7 @@ update_report(Req) ->
|
|||||||
send_error(Req2, 500, <<"Internal server error">>)
|
send_error(Req2, 500, <<"Internal server error">>)
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
send_error(Req2, 400, <<"Missing status field">>)
|
send_error(Req2, 400, <<"Missing status or reason">>)
|
||||||
catch
|
catch
|
||||||
_:_ -> send_error(Req2, 400, <<"Invalid JSON">>)
|
_:_ -> send_error(Req2, 400, <<"Invalid JSON">>)
|
||||||
end;
|
end;
|
||||||
@@ -60,6 +61,26 @@ update_report(Req) ->
|
|||||||
send_error(Req1, Code, Message)
|
send_error(Req1, Code, Message)
|
||||||
end.
|
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.
|
||||||
|
|
||||||
|
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,
|
||||||
|
<<"127.0.0.1">>, Reason);
|
||||||
|
_ -> ok
|
||||||
|
end.
|
||||||
|
|
||||||
report_to_json(R) ->
|
report_to_json(R) ->
|
||||||
#{
|
#{
|
||||||
id => R#report.id,
|
id => R#report.id,
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ init(Req, _Opts) ->
|
|||||||
case cowboy_req:method(Req) of
|
case cowboy_req:method(Req) of
|
||||||
<<"GET">> -> list_reports(Req);
|
<<"GET">> -> list_reports(Req);
|
||||||
<<"PUT">> -> update_report(Req);
|
<<"PUT">> -> update_report(Req);
|
||||||
_ -> send_error(Req, 405, <<"Method not allowed">>)
|
_ -> send_error(Req, 405, <<"Method not allowed">>)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
list_reports(Req) ->
|
list_reports(Req) ->
|
||||||
case handler_auth:authenticate(Req) of
|
case auth_admin(Req) of
|
||||||
{ok, AdminId, Req1} ->
|
{ok, AdminId, Req1} ->
|
||||||
case admin_utils:is_admin(AdminId) of
|
case admin_utils:is_admin(AdminId) of
|
||||||
true ->
|
true ->
|
||||||
@@ -26,16 +26,18 @@ list_reports(Req) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
update_report(Req) ->
|
update_report(Req) ->
|
||||||
case handler_auth:authenticate(Req) of
|
case auth_admin(Req) of
|
||||||
{ok, AdminId, Req1} ->
|
{ok, AdminId, Req1} ->
|
||||||
case admin_utils:is_admin(AdminId) of
|
case admin_utils:is_admin(AdminId) of
|
||||||
true ->
|
true ->
|
||||||
ReportId = cowboy_req:binding(id, Req1),
|
ReportId = cowboy_req:binding(id, Req1),
|
||||||
{ok, Body, Req2} = cowboy_req:read_body(Req1),
|
{ok, Body, Req2} = cowboy_req:read_body(Req1),
|
||||||
try jsx:decode(Body, [return_maps]) of
|
try jsx:decode(Body, [return_maps]) of
|
||||||
#{<<"status">> := NewStatus} ->
|
#{<<"status">> := NewStatus, <<"reason">> := Reason} ->
|
||||||
case core_report:update_status(ReportId, NewStatus, AdminId) of
|
StatusAtom = binary_to_atom(NewStatus, utf8),
|
||||||
|
case core_report:update_status(ReportId, StatusAtom, AdminId) of
|
||||||
{ok, Report} ->
|
{ok, Report} ->
|
||||||
|
log_audit(AdminId, <<"update_report_status">>, <<"report">>, ReportId, Reason),
|
||||||
send_json(Req2, 200, report_to_json(Report));
|
send_json(Req2, 200, report_to_json(Report));
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
send_error(Req2, 404, <<"Report not found">>);
|
send_error(Req2, 404, <<"Report not found">>);
|
||||||
@@ -43,7 +45,7 @@ update_report(Req) ->
|
|||||||
send_error(Req2, 500, <<"Internal server error">>)
|
send_error(Req2, 500, <<"Internal server error">>)
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
send_error(Req2, 400, <<"Missing status field">>)
|
send_error(Req2, 400, <<"Missing status or reason">>)
|
||||||
catch
|
catch
|
||||||
_:_ -> send_error(Req2, 400, <<"Invalid JSON">>)
|
_:_ -> send_error(Req2, 400, <<"Invalid JSON">>)
|
||||||
end;
|
end;
|
||||||
@@ -54,6 +56,26 @@ update_report(Req) ->
|
|||||||
send_error(Req1, Code, Message)
|
send_error(Req1, Code, Message)
|
||||||
end.
|
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.
|
||||||
|
|
||||||
|
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,
|
||||||
|
<<"127.0.0.1">>, Reason);
|
||||||
|
_ -> ok
|
||||||
|
end.
|
||||||
|
|
||||||
report_to_json(R) ->
|
report_to_json(R) ->
|
||||||
#{
|
#{
|
||||||
id => R#report.id,
|
id => R#report.id,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ test() ->
|
|||||||
AdminToken = api_test_runner:get_admin_token(),
|
AdminToken = api_test_runner:get_admin_token(),
|
||||||
UserToken = api_test_runner:get_user_token(),
|
UserToken = api_test_runner:get_user_token(),
|
||||||
|
|
||||||
%% Создаём календарь и событие через пользовательский API
|
%% Создаём календарь и событие
|
||||||
CalId = api_test_runner:extract_json(
|
CalId = api_test_runner:extract_json(
|
||||||
api_test_runner:http_post("/v1/calendars", #{title => <<"Mod Cal">>}, UserToken),
|
api_test_runner:http_post("/v1/calendars", #{title => <<"Mod Cal">>}, UserToken),
|
||||||
<<"id">>),
|
<<"id">>),
|
||||||
@@ -21,7 +21,7 @@ test() ->
|
|||||||
UserToken),
|
UserToken),
|
||||||
<<"id">>),
|
<<"id">>),
|
||||||
|
|
||||||
%% TEST 1: Create report (пользователь)
|
%% TEST 1: Create report
|
||||||
io:format(" TEST 1: Create report... "),
|
io:format(" TEST 1: Create report... "),
|
||||||
ReportId = api_test_runner:extract_json(
|
ReportId = api_test_runner:extract_json(
|
||||||
api_test_runner:http_post("/v1/reports",
|
api_test_runner:http_post("/v1/reports",
|
||||||
@@ -32,22 +32,22 @@ test() ->
|
|||||||
<<"id">>),
|
<<"id">>),
|
||||||
io:format("OK~n"),
|
io:format("OK~n"),
|
||||||
|
|
||||||
%% TEST 2: Admin views reports (через админский URL, прямой httpc)
|
%% TEST 2: Admin views reports
|
||||||
io:format(" TEST 2: Admin views reports... "),
|
io:format(" TEST 2: Admin views reports... "),
|
||||||
{ok, {{_, 200, _}, _, _}} = httpc:request(get,
|
{ok, {{_, 200, _}, _, _}} = httpc:request(get,
|
||||||
{?ADMIN_BASE_URL ++ "/v1/admin/reports", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []),
|
{?ADMIN_BASE_URL ++ "/v1/admin/reports", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []),
|
||||||
io:format("OK~n"),
|
io:format("OK~n"),
|
||||||
|
|
||||||
%% TEST 3: Admin resolves report
|
%% TEST 3: Admin resolves report с reason
|
||||||
io:format(" TEST 3: Admin resolves report... "),
|
io:format(" TEST 3: Admin resolves report... "),
|
||||||
{ok, {{_, 200, _}, _, _}} = httpc:request(put,
|
{ok, {{_, 200, _}, _, _}} = httpc:request(put,
|
||||||
{?ADMIN_BASE_URL ++ "/v1/admin/reports/" ++ binary_to_list(ReportId),
|
{?ADMIN_BASE_URL ++ "/v1/admin/reports/" ++ binary_to_list(ReportId),
|
||||||
[{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}],
|
[{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}],
|
||||||
"application/json",
|
"application/json",
|
||||||
jsx:encode(#{status => <<"reviewed">>})}, [], []),
|
jsx:encode(#{status => <<"reviewed">>, reason => <<"Resolved by moderator">>})}, [], []),
|
||||||
io:format("OK~n"),
|
io:format("OK~n"),
|
||||||
|
|
||||||
%% TEST 4: Add banned word (админ)
|
%% TEST 4: Add banned word
|
||||||
io:format(" TEST 4: Add banned word... "),
|
io:format(" TEST 4: Add banned word... "),
|
||||||
{ok, {{_, 201, _}, _, _}} = httpc:request(post,
|
{ok, {{_, 201, _}, _, _}} = httpc:request(post,
|
||||||
{?ADMIN_BASE_URL ++ "/v1/admin/banned-words",
|
{?ADMIN_BASE_URL ++ "/v1/admin/banned-words",
|
||||||
@@ -56,13 +56,13 @@ test() ->
|
|||||||
jsx:encode(#{<<"word">> => <<"badword">>})}, [], []),
|
jsx:encode(#{<<"word">> => <<"badword">>})}, [], []),
|
||||||
io:format("OK~n"),
|
io:format("OK~n"),
|
||||||
|
|
||||||
%% TEST 5: List banned words (админ)
|
%% TEST 5: List banned words
|
||||||
io:format(" TEST 5: List banned words... "),
|
io:format(" TEST 5: List banned words... "),
|
||||||
{ok, {{_, 200, _}, _, _}} = httpc:request(get,
|
{ok, {{_, 200, _}, _, _}} = httpc:request(get,
|
||||||
{?ADMIN_BASE_URL ++ "/v1/admin/banned-words", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []),
|
{?ADMIN_BASE_URL ++ "/v1/admin/banned-words", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []),
|
||||||
io:format("OK~n"),
|
io:format("OK~n"),
|
||||||
|
|
||||||
%% TEST 6: Remove banned word (админ)
|
%% TEST 6: Remove banned word
|
||||||
io:format(" TEST 6: Remove banned word... "),
|
io:format(" TEST 6: Remove banned word... "),
|
||||||
{ok, {{_, 200, _}, _, _}} = httpc:request(delete,
|
{ok, {{_, 200, _}, _, _}} = httpc:request(delete,
|
||||||
{?ADMIN_BASE_URL ++ "/v1/admin/banned-words/badword", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []),
|
{?ADMIN_BASE_URL ++ "/v1/admin/banned-words/badword", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []),
|
||||||
|
|||||||
Reference in New Issue
Block a user