Cybersecurity-Projects/PROJECTS/advanced/monitor-the-situation-dashb.../backend/internal/user/service.go

257 lines
4.7 KiB
Go

// AngelaMos | 2026
// service.go
package user
import (
"context"
"fmt"
"strings"
"github.com/google/uuid"
"github.com/carterperez-dev/monitor-the-situation/backend/internal/auth"
"github.com/carterperez-dev/monitor-the-situation/backend/internal/core"
)
type Service struct {
repo Repository
}
func NewService(repo Repository) *Service {
return &Service{repo: repo}
}
func (s *Service) GetByID(
ctx context.Context,
id string,
) (*auth.UserInfo, error) {
user, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
return toUserInfo(user), nil
}
func (s *Service) GetByEmail(
ctx context.Context,
email string,
) (*auth.UserInfo, error) {
user, err := s.repo.GetByEmail(ctx, strings.ToLower(email))
if err != nil {
return nil, err
}
return toUserInfo(user), nil
}
func (s *Service) Create(
ctx context.Context,
email, passwordHash, name string,
) (*auth.UserInfo, error) {
user := &User{
ID: uuid.New().String(),
Email: strings.ToLower(email),
PasswordHash: passwordHash,
Name: name,
Role: RoleUser,
Tier: TierFree,
}
if err := s.repo.Create(ctx, user); err != nil {
return nil, err
}
return toUserInfo(user), nil
}
func (s *Service) IncrementTokenVersion(
ctx context.Context,
userID string,
) error {
return s.repo.IncrementTokenVersion(ctx, userID)
}
func (s *Service) UpdatePassword(
ctx context.Context,
userID, passwordHash string,
) error {
return s.repo.UpdatePassword(ctx, userID, passwordHash)
}
func (s *Service) GetUser(ctx context.Context, id string) (*User, error) {
return s.repo.GetByID(ctx, id)
}
func (s *Service) UpdateUser(
ctx context.Context,
id string,
req UpdateUserRequest,
) (*User, error) {
user, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
if req.Name != nil {
user.Name = *req.Name
}
if err := s.repo.Update(ctx, user); err != nil {
return nil, err
}
return user, nil
}
func (s *Service) UpdateUserRole(
ctx context.Context,
id, role string,
) (*User, error) {
if role != RoleUser && role != RoleAdmin {
return nil, fmt.Errorf(
"update role: invalid role %q: %w",
role,
core.ErrInvalidInput,
)
}
user, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
user.Role = role
if err := s.repo.Update(ctx, user); err != nil {
return nil, err
}
return user, nil
}
func (s *Service) UpdateUserTier(
ctx context.Context,
id, tier string,
) (*User, error) {
if tier != TierFree && tier != TierPro && tier != TierEnterprise {
return nil, fmt.Errorf(
"update tier: invalid tier %q: %w",
tier,
core.ErrInvalidInput,
)
}
user, err := s.repo.GetByID(ctx, id)
if err != nil {
return nil, err
}
user.Tier = tier
if err := s.repo.Update(ctx, user); err != nil {
return nil, err
}
return user, nil
}
func (s *Service) DeleteUser(ctx context.Context, id string) error {
return s.repo.SoftDelete(ctx, id)
}
func (s *Service) ListUsers(
ctx context.Context,
params ListUsersParams,
) ([]User, int, error) {
return s.repo.List(ctx, params)
}
func (s *Service) GetMe(ctx context.Context, userID string) (*User, error) {
if userID == "" {
return nil, fmt.Errorf("get me: %w", core.ErrUnauthorized)
}
user, err := s.repo.GetByID(ctx, userID)
if err != nil {
return nil, err
}
return user, nil
}
func (s *Service) UpdateMe(
ctx context.Context,
userID string,
req UpdateUserRequest,
) (*User, error) {
if userID == "" {
return nil, fmt.Errorf("update me: %w", core.ErrUnauthorized)
}
return s.UpdateUser(ctx, userID, req)
}
func (s *Service) DeleteMe(ctx context.Context, userID string) error {
if userID == "" {
return fmt.Errorf("delete me: %w", core.ErrUnauthorized)
}
return s.repo.SoftDelete(ctx, userID)
}
func (s *Service) EmailExists(
ctx context.Context,
email string,
) (bool, error) {
exists, err := s.repo.ExistsByEmail(ctx, email)
if err != nil {
return false, err
}
return exists, nil
}
func (s *Service) CanDeleteUser(
ctx context.Context,
requesterID, targetID string,
) error {
if requesterID == targetID {
return nil
}
requester, err := s.repo.GetByID(ctx, requesterID)
if err != nil {
return err
}
if !requester.IsAdmin() {
return fmt.Errorf("delete user: %w", core.ErrForbidden)
}
target, err := s.repo.GetByID(ctx, targetID)
if err != nil {
return err
}
if target.IsAdmin() {
return fmt.Errorf("cannot delete admin users: %w", core.ErrForbidden)
}
return nil
}
func toUserInfo(u *User) *auth.UserInfo {
return &auth.UserInfo{
ID: u.ID,
Email: u.Email,
Name: u.Name,
PasswordHash: u.PasswordHash,
Role: u.Role,
Tier: u.Tier,
TokenVersion: u.TokenVersion,
}
}
var _ auth.UserProvider = (*Service)(nil)