package repository import ( "context" "errors" "time" "github.com/google/uuid" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "tm/internal/domain" "tm/pkg/logger" ) // UserRepositoryImpl implements the UserRepository interface type UserRepositoryImpl struct { collection *mongo.Collection logger logger.Logger } // NewUserRepository creates a new panel user repository func NewUserRepository(db *mongo.Database, logger logger.Logger) domain.UserRepository { collection := db.Collection("users") // Create indexes ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // Email index (unique) emailIndex := mongo.IndexModel{ Keys: bson.D{{"email", 1}}, Options: options.Index().SetUnique(true), } // Role index roleIndex := mongo.IndexModel{ Keys: bson.D{{"role", 1}}, } // Active status index activeIndex := mongo.IndexModel{ Keys: bson.D{{"is_active", 1}}, } // Department index departmentIndex := mongo.IndexModel{ Keys: bson.D{{"department", 1}}, } // Created at index createdAtIndex := mongo.IndexModel{ Keys: bson.D{{"created_at", -1}}, } _, err := collection.Indexes().CreateMany(ctx, []mongo.IndexModel{ emailIndex, roleIndex, activeIndex, departmentIndex, createdAtIndex, }) if err != nil { logger.Warn("Failed to create user indexes", map[string]interface{}{ "error": err.Error(), }) } return &UserRepositoryImpl{ collection: collection, logger: logger, } } // Create creates a new panel user func (r *UserRepositoryImpl) Create(ctx context.Context, user *domain.User) error { // Set created/updated timestamps now := time.Now() user.CreatedAt = now user.UpdatedAt = now // Insert user _, err := r.collection.InsertOne(ctx, user) if err != nil { if mongo.IsDuplicateKeyError(err) { return errors.New("user already exists") } r.logger.Error("Failed to create user", map[string]interface{}{ "error": err.Error(), "email": user.Email, "user_id": user.ID.String(), }) return err } r.logger.Info("Panel user created successfully", map[string]interface{}{ "user_id": user.ID.String(), "email": user.Email, "role": user.Role, }) return nil } // GetByID retrieves a panel user by ID func (r *UserRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*domain.User, error) { var user domain.User filter := bson.M{"_id": id} err := r.collection.FindOne(ctx, filter).Decode(&user) if err != nil { if err == mongo.ErrNoDocuments { return nil, errors.New("user not found") } r.logger.Error("Failed to get user by ID", map[string]interface{}{ "error": err.Error(), "user_id": id.String(), }) return nil, err } return &user, nil } // GetByEmail retrieves a panel user by email func (r *UserRepositoryImpl) GetByEmail(ctx context.Context, email string) (*domain.User, error) { var user domain.User filter := bson.M{"email": email} err := r.collection.FindOne(ctx, filter).Decode(&user) if err != nil { if err == mongo.ErrNoDocuments { return nil, errors.New("user not found") } r.logger.Error("Failed to get user by email", map[string]interface{}{ "error": err.Error(), "email": email, }) return nil, err } return &user, nil } // Update updates a panel user func (r *UserRepositoryImpl) Update(ctx context.Context, user *domain.User) error { // Set updated timestamp user.UpdatedAt = time.Now() filter := bson.M{"_id": user.ID} update := bson.M{"$set": user} result, err := r.collection.UpdateOne(ctx, filter, update) if err != nil { r.logger.Error("Failed to update user", map[string]interface{}{ "error": err.Error(), "user_id": user.ID.String(), }) return err } if result.MatchedCount == 0 { return errors.New("user not found") } r.logger.Info("Panel user updated successfully", map[string]interface{}{ "user_id": user.ID.String(), "email": user.Email, }) return nil } // Delete deletes a panel user (soft delete by setting is_active to false) func (r *UserRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error { filter := bson.M{"_id": id} update := bson.M{ "$set": bson.M{ "is_active": false, "updated_at": time.Now(), }, } result, err := r.collection.UpdateOne(ctx, filter, update) if err != nil { r.logger.Error("Failed to delete user", map[string]interface{}{ "error": err.Error(), "user_id": id.String(), }) return err } if result.MatchedCount == 0 { return errors.New("user not found") } r.logger.Info("Panel user deleted successfully", map[string]interface{}{ "user_id": id.String(), }) return nil } // List retrieves panel users with pagination func (r *UserRepositoryImpl) List(ctx context.Context, limit, offset int) ([]*domain.User, error) { var users []*domain.User // Build options opts := options.Find() opts.SetLimit(int64(limit)) opts.SetSkip(int64(offset)) opts.SetSort(bson.D{{"created_at", -1}}) // Sort by created_at desc // Only active users by default filter := bson.M{"is_active": true} cursor, err := r.collection.Find(ctx, filter, opts) if err != nil { r.logger.Error("Failed to list users", map[string]interface{}{ "error": err.Error(), "limit": limit, "offset": offset, }) return nil, err } defer cursor.Close(ctx) if err = cursor.All(ctx, &users); err != nil { r.logger.Error("Failed to decode users", map[string]interface{}{ "error": err.Error(), }) return nil, err } return users, nil } // GetByRole retrieves panel users by role with pagination func (r *UserRepositoryImpl) GetByRole(ctx context.Context, role domain.UserRole, limit, offset int) ([]*domain.User, error) { var users []*domain.User // Build options opts := options.Find() opts.SetLimit(int64(limit)) opts.SetSkip(int64(offset)) opts.SetSort(bson.D{{"created_at", -1}}) // Sort by created_at desc // Filter by role and active status filter := bson.M{ "role": role, "is_active": true, } cursor, err := r.collection.Find(ctx, filter, opts) if err != nil { r.logger.Error("Failed to get users by role", map[string]interface{}{ "error": err.Error(), "role": role, "limit": limit, "offset": offset, }) return nil, err } defer cursor.Close(ctx) if err = cursor.All(ctx, &users); err != nil { r.logger.Error("Failed to decode users by role", map[string]interface{}{ "error": err.Error(), "role": role, }) return nil, err } return users, nil } // UpdatePermissions updates panel user permissions func (r *UserRepositoryImpl) UpdatePermissions(ctx context.Context, id uuid.UUID, permissions []string) error { filter := bson.M{"_id": id} update := bson.M{ "$set": bson.M{ "permissions": permissions, "updated_at": time.Now(), }, } result, err := r.collection.UpdateOne(ctx, filter, update) if err != nil { r.logger.Error("Failed to update user permissions", map[string]interface{}{ "error": err.Error(), "user_id": id.String(), "permissions": permissions, }) return err } if result.MatchedCount == 0 { return errors.New("user not found") } r.logger.Info("Panel user permissions updated successfully", map[string]interface{}{ "user_id": id.String(), "permissions": permissions, }) return nil }