Реализовать модуль регистрации ботов #19
This commit is contained in:
15
Makefile
15
Makefile
@@ -307,6 +307,21 @@ docker-swarm-reg-admin:
|
|||||||
docker-swarm-check-admin:
|
docker-swarm-check-admin:
|
||||||
@docker exec $$(docker ps -qf "name=eventhub_eventhub" | head -n 1) /app/bin/eventhub eval 'eventhub_auth:authenticate_admin_request(<<"">>,<<"admin@eventhub.local">>,<<"123456">>).'
|
@docker exec $$(docker ps -qf "name=eventhub_eventhub" | head -n 1) /app/bin/eventhub eval 'eventhub_auth:authenticate_admin_request(<<"">>,<<"admin@eventhub.local">>,<<"123456">>).'
|
||||||
|
|
||||||
|
docker-swarm-reg-bots:
|
||||||
|
@docker exec $$(docker ps -qf "name=eventhub_eventhub" | head -n 1) /app/bin/eventhub eval "os:putenv(\"BOT_COUNT\", \"3000\"), bot_controller:register()."
|
||||||
|
|
||||||
|
docker-swarm-reg-bots-stop:
|
||||||
|
@docker exec $$(docker ps -qf "name=eventhub_eventhub" | head -n 1) /app/bin/eventhub eval 'bot_controller:stop().'
|
||||||
|
|
||||||
|
docker-swarm-delete-bots:
|
||||||
|
@docker exec $$(docker ps -qf "name=eventhub_eventhub" | head -n 1) /app/bin/eventhub eval 'bot_controller:delete().'
|
||||||
|
|
||||||
|
docker-swarm-count-bots:
|
||||||
|
@for id in $$(docker ps -qf "name=eventhub_eventhub"); do \
|
||||||
|
echo "=== $$id ==="; \
|
||||||
|
docker exec $$id /app/bin/eventhub eval 'bot_controller:count_bots().'; \
|
||||||
|
done
|
||||||
|
|
||||||
docker-swarm-shell:
|
docker-swarm-shell:
|
||||||
@docker exec $$(docker ps -qf "name=eventhub_eventhub" | head -n 1) /app/bin/eventhub remote_console
|
@docker exec $$(docker ps -qf "name=eventhub_eventhub" | head -n 1) /app/bin/eventhub remote_console
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
-export([list_users/0]).
|
-export([list_users/0]).
|
||||||
-export([block/2, unblock/2]).
|
-export([block/2, unblock/2]).
|
||||||
-export([count_users/0, count_users_by_date/2]).
|
-export([count_users/0, count_users_by_date/2]).
|
||||||
|
-export([create_bot/2, delete_bot/1]).
|
||||||
|
|
||||||
%% Создание пользователя
|
%% Создание пользователя
|
||||||
create(Email, Password) ->
|
create(Email, Password) ->
|
||||||
@@ -170,4 +171,44 @@ set_field(email, Value, U) -> U#user{email = Value};
|
|||||||
set_field(password_hash, Value, U) -> U#user{password_hash = Value};
|
set_field(password_hash, Value, U) -> U#user{password_hash = Value};
|
||||||
set_field(role, Value, U) when Value =:= user; Value =:= admin -> U#user{role = Value};
|
set_field(role, Value, U) when Value =:= user; Value =:= admin -> U#user{role = Value};
|
||||||
set_field(status, Value, U) when Value =:= active; Value =:= frozen; Value =:= deleted -> U#user{status = Value};
|
set_field(status, Value, U) when Value =:= active; Value =:= frozen; Value =:= deleted -> U#user{status = Value};
|
||||||
set_field(_, _, U) -> U.
|
set_field(_, _, U) -> U.
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% API для ботов
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec create_bot(Email :: binary(), Password :: binary()) ->
|
||||||
|
{ok, User :: #user{}} | {error, duplicate_email | invalid_email}.
|
||||||
|
create_bot(Email, Password) ->
|
||||||
|
case email_exists(Email) of
|
||||||
|
true ->
|
||||||
|
{error, email_exists};
|
||||||
|
false ->
|
||||||
|
case mnesia:dirty_index_read(user, Email, email) of
|
||||||
|
[] ->
|
||||||
|
{ok, PasswordHash} = logic_auth:hash_password(Password),
|
||||||
|
Id = generate_id(),
|
||||||
|
User = #user{
|
||||||
|
id = Id,
|
||||||
|
email = Email,
|
||||||
|
password_hash = PasswordHash,
|
||||||
|
role = bot,
|
||||||
|
status = active,
|
||||||
|
created_at = calendar:universal_time(),
|
||||||
|
updated_at = calendar:universal_time()
|
||||||
|
},
|
||||||
|
ok = mnesia:dirty_write(User),
|
||||||
|
{ok, User};
|
||||||
|
_ ->
|
||||||
|
{error, duplicate_email}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec delete_bot(Id :: binary()) -> ok | {error, not_found}.
|
||||||
|
delete_bot(Id) ->
|
||||||
|
case mnesia:dirty_read({user, Id}) of
|
||||||
|
[#user{role = bot}] ->
|
||||||
|
mnesia:dirty_delete({user, Id}),
|
||||||
|
ok;
|
||||||
|
[] -> {error, not_found}
|
||||||
|
end.
|
||||||
88
src/infra/bot_controller.erl
Normal file
88
src/infra/bot_controller.erl
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
-module(bot_controller).
|
||||||
|
-include("records.hrl").
|
||||||
|
|
||||||
|
-export([register/0, stop/0, delete/0, count_bots/0]).
|
||||||
|
|
||||||
|
-define(REG_NAME, {bot_registration, node()}).
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% @doc Асинхронно регистрирует ботов (одновременно только один процесс).
|
||||||
|
%% Возвращает pid процесса или ошибку.
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
register() ->
|
||||||
|
Count = list_to_integer(os:getenv("BOT_COUNT", "0")),
|
||||||
|
Domain = list_to_binary(os:getenv("BOT_DOMAIN", "eventhub.com")),
|
||||||
|
Password = list_to_binary(os:getenv("BOT_DEFAULT_PASSWORD", "botpass123")),
|
||||||
|
if Count =< 0 ->
|
||||||
|
{error, <<"BOT_COUNT must be greater than 0">>};
|
||||||
|
true ->
|
||||||
|
io:format("Starting async registration of ~p bots with domain ~s...~n", [Count, Domain]),
|
||||||
|
Pid = spawn(fun() ->
|
||||||
|
register_bots(Count, Domain, Password)
|
||||||
|
end),
|
||||||
|
case global:register_name(?REG_NAME, Pid) of
|
||||||
|
yes ->
|
||||||
|
{ok, Pid};
|
||||||
|
no ->
|
||||||
|
% Уже есть активный процесс регистрации
|
||||||
|
exit(Pid, kill),
|
||||||
|
{error, registration_already_running}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% @doc Останавливает текущий процесс регистрации.
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
stop() ->
|
||||||
|
case global:whereis_name(?REG_NAME) of
|
||||||
|
undefined ->
|
||||||
|
io:format("No active bot registration found.~n");
|
||||||
|
Pid ->
|
||||||
|
exit(Pid, kill),
|
||||||
|
io:format("Bot registration process ~p stopped.~n", [Pid])
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% @doc Удаляет всех пользователей с ролью bot.
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
delete() ->
|
||||||
|
Bots = mnesia:dirty_match_object(#user{role = bot, _ = '_'}),
|
||||||
|
lists:foreach(fun(#user{id = Id}) ->
|
||||||
|
mnesia:dirty_delete({user, Id})
|
||||||
|
end, Bots),
|
||||||
|
io:format("Deleted ~p bots.~n", [length(Bots)]),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% @doc Возвращает количество ботов в базе.
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
count_bots() ->
|
||||||
|
length(mnesia:dirty_match_object(#user{role = bot, _ = '_'})).
|
||||||
|
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
%% Внутренние функции
|
||||||
|
%% ------------------------------------------------------------------
|
||||||
|
register_bots(Count, Domain, Password) ->
|
||||||
|
register_bots(Count, Domain, Password, []).
|
||||||
|
|
||||||
|
register_bots(0, _, _, Acc) ->
|
||||||
|
io:format("Async registration completed. Created ~p bots.~n", [length(Acc)]),
|
||||||
|
global:unregister_name(?REG_NAME);
|
||||||
|
register_bots(N, Domain, Password, Acc) ->
|
||||||
|
Email = generate_email(Domain),
|
||||||
|
case core_user:create_bot(Email, Password) of
|
||||||
|
{ok, _User} ->
|
||||||
|
register_bots(N-1, Domain, Password, [Email | Acc]);
|
||||||
|
{error, duplicate_email} ->
|
||||||
|
register_bots(N, Domain, Password, Acc);
|
||||||
|
{error, _} ->
|
||||||
|
register_bots(N-1, Domain, Password, Acc)
|
||||||
|
end.
|
||||||
|
|
||||||
|
generate_email(Domain) ->
|
||||||
|
Name = random_string(8),
|
||||||
|
<<"bot_", Name/binary, "@", Domain/binary>>.
|
||||||
|
|
||||||
|
random_string(Length) ->
|
||||||
|
Chars = <<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789">>,
|
||||||
|
list_to_binary([lists:nth(rand:uniform(byte_size(Chars)), binary_to_list(Chars)) || _ <- lists:seq(1, Length)]).
|
||||||
@@ -242,6 +242,7 @@ create_indices() ->
|
|||||||
mnesia:add_table_index(calendar_specialist, calendar_id),
|
mnesia:add_table_index(calendar_specialist, calendar_id),
|
||||||
mnesia:add_table_index(calendar_specialist, user_id),
|
mnesia:add_table_index(calendar_specialist, user_id),
|
||||||
mnesia:add_table_index(user, nickname),
|
mnesia:add_table_index(user, nickname),
|
||||||
|
mnesia:add_table_index(user, email),
|
||||||
mnesia:add_table_index(notification, user_id),
|
mnesia:add_table_index(notification, user_id),
|
||||||
mnesia:add_table_index(notification, is_read),
|
mnesia:add_table_index(notification, is_read),
|
||||||
ok.
|
ok.
|
||||||
Reference in New Issue
Block a user