Улучшение безопасности и обработки ошибок. Этап 1. #8

This commit is contained in:
2026-04-29 11:00:14 +03:00
parent 3da4ee28d4
commit cfd2e38952
10 changed files with 219 additions and 116 deletions
+14 -19
View File
@@ -1,6 +1,6 @@
-module(core_admin_audit).
-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]).
log(AdminId, Email, Role, Action, EntityType, EntityId, Ip) ->
@@ -25,33 +25,28 @@ log(AdminId, Email, Role, Action, EntityType, EntityId, Ip, Reason) ->
list() ->
mnesia:dirty_match_object(#admin_audit{_ = '_'}).
%% Фильтрация по параметрам (простая версия)
list(Filters) ->
All = list(), % все записи
All = list(),
lists:filter(fun(E) ->
% Фильтр по admin_id
case proplists:get_value(admin_id, Filters) of
undefined -> true;
Id -> E#admin_audit.admin_id =:= Id
end
andalso
% Фильтр по action
case proplists:get_value(action, Filters) of
undefined -> true;
Act -> E#admin_audit.action =:= Act
end
case proplists:get_value(action, Filters) of
undefined -> true;
Act -> E#admin_audit.action =:= Act
end
andalso
% Фильтр по дате с
case proplists:get_value(date_from, Filters) of
undefined -> true;
From -> E#admin_audit.timestamp >= From
end
case proplists:get_value(date_from, Filters) of
undefined -> true;
From -> E#admin_audit.timestamp >= From
end
andalso
% Фильтр по дате по
case proplists:get_value(date_to, Filters) of
undefined -> true;
To -> E#admin_audit.timestamp =< To
end
case proplists:get_value(date_to, Filters) of
undefined -> true;
To -> E#admin_audit.timestamp =< To
end
end, All).
count_actions_by_admin(AdminId, Action) ->
+15 -17
View File
@@ -1,9 +1,9 @@
-module(core_calendar).
-include("records.hrl").
-export([create/4, create/5, get_by_id/1, list_by_owner/1, update/2, delete/1]).
-export([generate_id/0]).
-export([count_calendars/0]).
-export([freeze/2, unfreeze/2]). % ← новые функции
%% Создание календаря
create(OwnerId, Title, Description, Confirmation) ->
@@ -22,12 +22,7 @@ create(OwnerId, Title, Description, Confirmation) ->
created_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
{atomic, Result} -> Result;
{aborted, Reason} -> {error, Reason}
@@ -50,12 +45,7 @@ create(OwnerId, Title, Description, Confirmation, Type) ->
created_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
{atomic, Result} -> Result;
{aborted, Reason} -> {error, Reason}
@@ -78,15 +68,13 @@ list_by_owner(OwnerId) ->
update(Id, Updates) ->
F = fun() ->
case mnesia:read(calendar, Id) of
[] ->
{error, not_found};
[] -> {error, not_found};
[Calendar] ->
UpdatedCalendar = apply_updates(Calendar, Updates),
mnesia:write(UpdatedCalendar),
{ok, UpdatedCalendar}
end
end,
case mnesia:transaction(F) of
{atomic, Result} -> Result;
{aborted, Reason} -> {error, Reason}
@@ -96,11 +84,20 @@ update(Id, Updates) ->
delete(Id) ->
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() ->
base64:encode(crypto:strong_rand_bytes(16), #{mode => urlsafe, padding => false}).
apply_updates(Calendar, Updates) ->
Updated = lists:foldl(fun({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(rating_avg, Value, C) -> C#calendar{rating_avg = 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.
+9
View File
@@ -5,6 +5,7 @@
update/2, delete/1, materialize_occurrence/3]).
-export([generate_id/0]).
-export([count_events/0, count_events_by_date/2]).
-export([freeze/2, unfreeze/2]).
%% Создание одиночного события
create(CalendarId, Title, StartTime, Duration) ->
@@ -197,6 +198,13 @@ apply_updates(Event, Updates) ->
end, Event, Updates),
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(description, Value, E) -> E#event{description = 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(online_link, Value, E) -> E#event{online_link = 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_count, Value, E) -> E#event{rating_count = Value};
set_field(_, _, E) -> E.
+6 -6
View File
@@ -2,7 +2,7 @@
-include("records.hrl").
-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([generate_id/0]).
-export([count_reviews/0]).
@@ -87,12 +87,11 @@ delete(Id) ->
end.
%% Скрытие отзыва (модерация)
hide(Id) ->
update(Id, [{status, hidden}]).
hide(Id, Reason) ->
update(Id, [{status, hidden}, {reason, Reason}]).
%% Раскрытие отзыва
unhide(Id) ->
update(Id, [{status, visible}]).
unhide(Id, Reason) ->
update(Id, [{status, visible}, {reason, Reason}]).
%% Получение среднего рейтинга цели
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(comment, Value, R) -> R#review{comment = 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.
+31 -11
View File
@@ -1,11 +1,11 @@
-module(core_user).
-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([generate_id/0]).
-export([list_users/0]).
-export([block/1, unblock/1]).
-export([block/2, unblock/2]).
-export([count_users/0, count_users_by_date/2]).
%% Создание пользователя
@@ -85,6 +85,15 @@ update(Id, Updates) ->
{aborted, Reason} -> {error, Reason}
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)
delete(Id) ->
update(Id, [{status, deleted}]).
@@ -94,30 +103,41 @@ list_users() ->
ActiveUsers = [U || U <- Users, U#user.status =/= deleted],
{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) ->
#{
id => User#user.id,
email => User#user.email,
password_hash => User#user.password_hash,
role => User#user.role,
status => User#user.status,
id => User#user.id,
email => User#user.email,
role => atom_to_binary(User#user.role, utf8),
status => atom_to_binary(User#user.status, utf8),
reason => User#user.reason,
created_at => User#user.created_at,
updated_at => User#user.updated_at
}.
block(Id) ->
block(Id, Reason) ->
case get_by_id(Id) of
{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),
{ok, Updated};
Error -> Error
end.
unblock(Id) ->
unblock(Id, Reason) ->
case get_by_id(Id) of
{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),
{ok, Updated};
Error -> Error