Merge branch 'develop' into feature/ted
This commit is contained in:
@@ -178,7 +178,16 @@ func (r *customerRepository) GetByUsername(ctx context.Context, username string)
|
|||||||
|
|
||||||
// GetByIDs retrieves customers by IDs
|
// GetByIDs retrieves customers by IDs
|
||||||
func (r *customerRepository) GetByIDs(ctx context.Context, ids []string) ([]Customer, error) {
|
func (r *customerRepository) GetByIDs(ctx context.Context, ids []string) ([]Customer, error) {
|
||||||
customers, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": ids}}, orm.NewPaginationBuilder().Limit(len(ids)).Build())
|
objectIDs := make([]bson.ObjectID, 0)
|
||||||
|
for _, id := range ids {
|
||||||
|
objectID, err := bson.ObjectIDFromHex(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objectIDs = append(objectIDs, objectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
customers, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": objectIDs}}, orm.NewPaginationBuilder().Limit(len(ids)).Build())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, orm.ErrDocumentNotFound) {
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
||||||
return nil, errors.New("customers not found")
|
return nil, errors.New("customers not found")
|
||||||
@@ -211,7 +220,16 @@ func (r *customerRepository) GetByRole(ctx context.Context, role CustomerRole) (
|
|||||||
// GetByCompanies retrieves customers by companies
|
// GetByCompanies retrieves customers by companies
|
||||||
|
|
||||||
func (r *customerRepository) GetByCompanies(ctx context.Context, companies []string) ([]Customer, error) {
|
func (r *customerRepository) GetByCompanies(ctx context.Context, companies []string) ([]Customer, error) {
|
||||||
customers, err := r.ormRepo.FindAll(ctx, bson.M{"companies": bson.M{"$in": companies}}, orm.NewPaginationBuilder().Build())
|
companyIDs := make([]bson.ObjectID, 0)
|
||||||
|
for _, company := range companies {
|
||||||
|
id, err := bson.ObjectIDFromHex(company)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
companyIDs = append(companyIDs, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
customers, err := r.ormRepo.FindAll(ctx, bson.M{"companies": bson.M{"$in": companyIDs}}, orm.NewPaginationBuilder().Build())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ func (s *customerService) Register(ctx context.Context, form *CreateCustomerForm
|
|||||||
"company_ids": Companies,
|
"company_ids": Companies,
|
||||||
})
|
})
|
||||||
|
|
||||||
s.notification.SendEmail(ctx, notification.Model{
|
go s.notification.SendEmail(context.Background(), notification.Model{
|
||||||
Recipient: customer.Email,
|
Recipient: customer.Email,
|
||||||
Title: "Welcome to Opplens",
|
Title: "Welcome to Opplens",
|
||||||
Message: fmt.Sprintf("Welcome to Opplens\nUsername: %s\nPassword: %s", strings.ToLower(customer.Username), form.Password),
|
Message: fmt.Sprintf("Welcome to Opplens\nUsername: %s\nPassword: %s", strings.ToLower(customer.Username), form.Password),
|
||||||
@@ -322,6 +322,7 @@ func (s *customerService) Update(ctx context.Context, id string, form *UpdateCus
|
|||||||
return nil, errors.New("invalid username format")
|
return nil, errors.New("invalid username format")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeUsername := false
|
||||||
// Check if username already exists (if changing username)
|
// Check if username already exists (if changing username)
|
||||||
if form.Username != "" && strings.ToLower(form.Username) != customer.Username {
|
if form.Username != "" && strings.ToLower(form.Username) != customer.Username {
|
||||||
existingCustomer, _ := s.repository.GetByUsername(ctx, strings.ToLower(form.Username))
|
existingCustomer, _ := s.repository.GetByUsername(ctx, strings.ToLower(form.Username))
|
||||||
@@ -329,6 +330,7 @@ func (s *customerService) Update(ctx context.Context, id string, form *UpdateCus
|
|||||||
return nil, errors.New("customer with this username already exists")
|
return nil, errors.New("customer with this username already exists")
|
||||||
}
|
}
|
||||||
customer.Username = strings.ToLower(form.Username)
|
customer.Username = strings.ToLower(form.Username)
|
||||||
|
changeUsername = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle company assignments
|
// Handle company assignments
|
||||||
@@ -366,6 +368,16 @@ func (s *customerService) Update(ctx context.Context, id string, form *UpdateCus
|
|||||||
return nil, errors.New("failed to update customer")
|
return nil, errors.New("failed to update customer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if changeUsername {
|
||||||
|
go s.notification.SendEmail(context.Background(), notification.Model{
|
||||||
|
Recipient: customer.Email,
|
||||||
|
Title: "Username Updated",
|
||||||
|
Message: fmt.Sprintf("Your username has been updated to %s", customer.Username),
|
||||||
|
Type: "info",
|
||||||
|
Priority: "important",
|
||||||
|
UserID: customer.ID.Hex(),
|
||||||
|
})
|
||||||
|
}
|
||||||
s.logger.Info("Customer updated successfully", map[string]interface{}{
|
s.logger.Info("Customer updated successfully", map[string]interface{}{
|
||||||
"customer_id": customer.ID,
|
"customer_id": customer.ID,
|
||||||
"email": customer.Email,
|
"email": customer.Email,
|
||||||
@@ -428,7 +440,7 @@ func (s *customerService) UpdateStatus(ctx context.Context, id string, form *Upd
|
|||||||
"status": form.Status,
|
"status": form.Status,
|
||||||
})
|
})
|
||||||
|
|
||||||
s.notification.SendEmail(ctx, notification.Model{
|
go s.notification.SendEmail(context.Background(), notification.Model{
|
||||||
Recipient: customer.Email,
|
Recipient: customer.Email,
|
||||||
Title: "Account Status Updated",
|
Title: "Account Status Updated",
|
||||||
Message: fmt.Sprintf("Your account has been %s", strings.ToUpper(form.Status)),
|
Message: fmt.Sprintf("Your account has been %s", strings.ToUpper(form.Status)),
|
||||||
@@ -779,18 +791,37 @@ func (s *customerService) GetProfileWithCompanies(ctx context.Context, customerI
|
|||||||
// Logout handles customer logout
|
// Logout handles customer logout
|
||||||
func (s *customerService) Logout(ctx context.Context, customerID string, accessToken string) error {
|
func (s *customerService) Logout(ctx context.Context, customerID string, accessToken string) error {
|
||||||
s.logger.Info("Customer logout", map[string]interface{}{
|
s.logger.Info("Customer logout", map[string]interface{}{
|
||||||
"customer_id": customerID,
|
"customer_id": customerID,
|
||||||
"access_token": accessToken[:10] + "...", // Log only first 10 chars for security
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// get customer by id
|
||||||
|
customer, err := s.repository.GetByID(ctx, customerID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get customer for logout", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"customer_id": customerID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Invalidate the access token
|
// Invalidate the access token
|
||||||
err := s.authService.InvalidateToken(accessToken)
|
err = s.authService.InvalidateToken(accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to invalidate access token", map[string]interface{}{
|
s.logger.Error("Failed to invalidate access token", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"customer_id": customerID,
|
"customer_id": customerID,
|
||||||
})
|
})
|
||||||
// Don't return error here as the customer should still be logged out
|
// Don't return error here as the customer should still be logged out
|
||||||
|
return errors.New("failed to invalidate access token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete device token from customer
|
||||||
|
customer.DeviceToken = make([]string, 0)
|
||||||
|
err = s.repository.Update(ctx, customer)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update customer device token", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"customer_id": customerID,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("Customer logout successful", map[string]interface{}{
|
s.logger.Info("Customer logout successful", map[string]interface{}{
|
||||||
@@ -869,7 +900,7 @@ func (s *customerService) RequestResetPassword(ctx context.Context, form *Reques
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send OTP via email
|
// Send OTP via email
|
||||||
s.notification.SendEmail(ctx, notification.Model{
|
go s.notification.SendEmail(context.Background(), notification.Model{
|
||||||
Recipient: form.Email,
|
Recipient: form.Email,
|
||||||
Title: "Password Reset",
|
Title: "Password Reset",
|
||||||
Message: fmt.Sprintf("Your password reset code is: %s", otpCode),
|
Message: fmt.Sprintf("Your password reset code is: %s", otpCode),
|
||||||
@@ -1064,7 +1095,7 @@ func (s *customerService) ResetPassword(ctx context.Context, form *ResetPassword
|
|||||||
"customer_id": customer.ID,
|
"customer_id": customer.ID,
|
||||||
})
|
})
|
||||||
|
|
||||||
s.notification.SendEmail(ctx, notification.Model{
|
go s.notification.SendEmail(context.Background(), notification.Model{
|
||||||
Recipient: tokenData.Email,
|
Recipient: tokenData.Email,
|
||||||
Title: "Password Reset Success",
|
Title: "Password Reset Success",
|
||||||
Message: "Your password has been reset successfully",
|
Message: "Your password has been reset successfully",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package notification
|
package notification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
|
||||||
@@ -48,7 +50,7 @@ func (h *NotificationHandler) Send(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send notification
|
// Send notification
|
||||||
h.service.Send(c.Request().Context(), &req)
|
go h.service.Send(context.Background(), &req)
|
||||||
|
|
||||||
return response.Created(c, nil, "Notification sent successfully")
|
return response.Created(c, nil, "Notification sent successfully")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -397,6 +397,10 @@ func (s *notificationService) getCustomers(ctx context.Context, values []string,
|
|||||||
recipients := make([]notificationRecipient, 0)
|
recipients := make([]notificationRecipient, 0)
|
||||||
|
|
||||||
if target == string(TargetAudienceSpecificRole) {
|
if target == string(TargetAudienceSpecificRole) {
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil, errors.New("no role provided")
|
||||||
|
}
|
||||||
|
|
||||||
customers, err := s.customerService.GetCustomersByRole(ctx, values[0])
|
customers, err := s.customerService.GetCustomersByRole(ctx, values[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -218,7 +218,16 @@ func (r *userRepository) Delete(ctx context.Context, id string) error {
|
|||||||
|
|
||||||
// GetByIDs retrieves users by IDs
|
// GetByIDs retrieves users by IDs
|
||||||
func (r *userRepository) GetByIDs(ctx context.Context, userIDs []string) ([]User, error) {
|
func (r *userRepository) GetByIDs(ctx context.Context, userIDs []string) ([]User, error) {
|
||||||
users, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": userIDs}}, orm.NewPaginationBuilder().Limit(len(userIDs)).Build())
|
objectIDs := make([]bson.ObjectID, 0)
|
||||||
|
for _, id := range userIDs {
|
||||||
|
objectID, err := bson.ObjectIDFromHex(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objectIDs = append(objectIDs, objectID)
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": objectIDs}}, orm.NewPaginationBuilder().Limit(len(userIDs)).Build())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ func (s *userService) Register(ctx context.Context, form *CreateUserForm) (*User
|
|||||||
"role": user.Role,
|
"role": user.Role,
|
||||||
})
|
})
|
||||||
|
|
||||||
s.notification.SendEmail(ctx, notification.Model{
|
go s.notification.SendEmail(context.Background(), notification.Model{
|
||||||
Recipient: form.Email,
|
Recipient: form.Email,
|
||||||
Title: "Welcome to Opplens",
|
Title: "Welcome to Opplens",
|
||||||
Message: fmt.Sprintf("Welcome to Opplens\nUsername: %s\nPassword: %s", strings.ToLower(form.Username), form.Password),
|
Message: fmt.Sprintf("Welcome to Opplens\nUsername: %s\nPassword: %s", strings.ToLower(form.Username), form.Password),
|
||||||
@@ -229,7 +229,7 @@ func (s *userService) UpdateStatus(ctx context.Context, id string, form *UpdateU
|
|||||||
"status": form.Status,
|
"status": form.Status,
|
||||||
})
|
})
|
||||||
|
|
||||||
s.notification.SendEmail(ctx, notification.Model{
|
go s.notification.SendEmail(context.Background(), notification.Model{
|
||||||
Recipient: user.Email,
|
Recipient: user.Email,
|
||||||
Title: "Account Status Updated",
|
Title: "Account Status Updated",
|
||||||
Message: fmt.Sprintf("Your account has been %s", strings.ToUpper(form.Status)),
|
Message: fmt.Sprintf("Your account has been %s", strings.ToUpper(form.Status)),
|
||||||
@@ -495,7 +495,7 @@ func (s *userService) ChangePassword(ctx context.Context, userID string, form *C
|
|||||||
"user_id": userID,
|
"user_id": userID,
|
||||||
})
|
})
|
||||||
|
|
||||||
s.notification.SendEmail(ctx, notification.Model{
|
go s.notification.SendEmail(context.Background(), notification.Model{
|
||||||
Recipient: user.Email,
|
Recipient: user.Email,
|
||||||
Title: "Password Reset Success",
|
Title: "Password Reset Success",
|
||||||
Message: "Your password has been reset successfully",
|
Message: "Your password has been reset successfully",
|
||||||
@@ -632,14 +632,33 @@ func (s *userService) UpdateRole(ctx context.Context, id string, form *UpdateUse
|
|||||||
|
|
||||||
// Logout logs out a user and invalidates tokens
|
// Logout logs out a user and invalidates tokens
|
||||||
func (s *userService) Logout(ctx context.Context, userID string, accessToken string) error {
|
func (s *userService) Logout(ctx context.Context, userID string, accessToken string) error {
|
||||||
|
// get user by id
|
||||||
|
user, err := s.repository.GetByID(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get user for logout", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": userID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Invalidate the access token
|
// Invalidate the access token
|
||||||
err := s.authService.InvalidateToken(accessToken)
|
err = s.authService.InvalidateToken(accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to invalidate access token", map[string]interface{}{
|
s.logger.Error("Failed to invalidate access token", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"user_id": userID,
|
"user_id": userID,
|
||||||
})
|
})
|
||||||
// Don't return error here as the user should still be logged out
|
return errors.New("failed to invalidate access token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete device token from user
|
||||||
|
user.DeviceToken = make([]string, 0)
|
||||||
|
err = s.repository.Update(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update user device token", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": userID,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("User logged out successfully", map[string]interface{}{
|
s.logger.Info("User logged out successfully", map[string]interface{}{
|
||||||
|
|||||||
Reference in New Issue
Block a user