This commit is contained in:
2026-08-19 02:15:57 +07:00
parent 72605a3cd4
commit 204a20c814
25 changed files with 2892 additions and 500 deletions
+58 -1
View File
@@ -9,6 +9,7 @@ import (
"evening_detective_server/internal/modules/roles"
"evening_detective_server/internal/repos"
"evening_detective_server/internal/repos/refresh_tokens_repo"
"evening_detective_server/internal/repos/user_agreements_repo"
"evening_detective_server/internal/repos/users_repo"
"fmt"
"slices"
@@ -17,6 +18,17 @@ import (
"golang.org/x/crypto/bcrypt"
)
// Версии документов, акцепт которых фиксируется. Должны совпадать с
// редакциями, опубликованными по постоянным URL (/terms и /privacy,
// rpc GetTerms/GetPrivacy, текст встраивается из пакета docs): при изменении
// текста документа версия ОБЯЗАНА увеличиваться, иначе повторный акцепт
// не будет зафиксирован как согласие с новой редакцией
// (UNIQUE (user_id, agreement_type, version)).
const (
termsVersion = "1.0"
privacyVersion = "1.0"
)
type UsersService struct {
usersRepo *users_repo.UsersRepo
passwordGenerator password_generator.IPasswordGenerator
@@ -41,11 +53,22 @@ func NewUsersService(
}
}
// ErrTermsNotAccepted — регистрация невозможна без акцепта соглашений.
var ErrTermsNotAccepted = errors.New("Необходимо принять Пользовательское соглашение и Политику конфиденциальности")
func (s *UsersService) AddUser(
ctx context.Context,
username string,
email string,
acceptTerms bool,
acceptPrivacy bool,
ip string,
userAgent string,
) error {
if !acceptTerms || !acceptPrivacy {
return ErrTermsNotAccepted
}
password, err := s.passwordGenerator.Generate()
if err != nil {
return err
@@ -54,12 +77,28 @@ func (s *UsersService) AddUser(
if err != nil {
return err
}
err = s.usersRepo.AddUser(
// Создание пользователя и фиксация акцептов — в одной транзакции:
// без записей о согласиях пользователь не существует.
_, err = s.usersRepo.AddUserWithAgreements(
ctx,
username,
email,
string(hashedPassword),
[]string{"user"},
[]user_agreements_repo.Agreement{
{
AgreementType: user_agreements_repo.AgreementTypeTerms,
Version: termsVersion,
IP: ip,
UserAgent: userAgent,
},
{
AgreementType: user_agreements_repo.AgreementTypePrivacy,
Version: privacyVersion,
IP: ip,
UserAgent: userAgent,
},
},
)
if err != nil {
return err
@@ -74,6 +113,24 @@ func (s *UsersService) AddUser(
return err
}
// DeleteAccount удаляет учётную запись и все персональные данные пользователя
// (ст. 21 152-ФЗ). Требует подтверждения паролем: обладатель украденного
// access-токена не должен мочь удалить чужой аккаунт.
func (s *UsersService) DeleteAccount(
ctx context.Context,
userId int,
password string,
) error {
user, err := s.usersRepo.GetUserByID(ctx, userId)
if err != nil {
return err
}
if err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil {
return errors.New("Неверный пароль")
}
return s.usersRepo.DeleteUser(ctx, userId)
}
func (s *UsersService) RefreshPassword(
ctx context.Context,
email string,