Skip to content

Conversation

@ChayanDass
Copy link
Contributor

@ChayanDass ChayanDass commented Aug 21, 2025

Bulk Import

image

license update

image final_message_system_workflow_polished

functions cover

  • license update
  • license create
  • license import
  • also for populatedb

note : currently, only the license changes are included

@avinal
Copy link
Member

avinal commented Sep 30, 2025

@ChayanDass Please follow good commits and PR practices:

  • the title is totally non-conclusive of what this PR is doing
  • Use rebase and amends instead of merging "main"
  • use proper heading decoration in PR body
  • always sign off your commits and try to keep them in sync with the PR description

Refer: https://cbea.ms/git-commit/#seven-rules

@deo002 deo002 changed the title masseging system Messaging system Oct 28, 2025
return service
}

func (s *AsyncEmailService) Send(to, subject, html string) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a small refactor. Let's write interfaces and their methods together.


service := &AsyncEmailService{
emailQueue: make(chan EmailData, emailQueueSize),
licenseQueue: make(chan LicenseJob, licenseQueueSize),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to take a more generic approach by using only the emailQueue and not having separate licenseQueue and bulkQueue as they ultimately send the jobs to emailQueue after formatting data into email format.

Right now, the flow is:
LicenseJob ---> licenseQueue ---> formatted EmailData ---> emailQueue
BulkJob ---> bulkQueue ---> formatted EmailData ---> emailQueue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve implemented a wrapper function to simplify the process.
Now, the email service works as a standalone service, the controller only calls the wrapper to create the email and then pushes it to the email queue.

Using the wrapper keeps the controller clean and avoids unnecessary logic there.

now in future , if need more email types , then creating a new templete would be enough , no need to chnge on the email service ..

zap.Int("success", job.Success),
zap.Int("failed", job.Failed),
)
s.bulkQueue <- job
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call Send() directly from where we currently enqueue licenseQueue and bulkQueue.

Copy link
Contributor Author

@ChayanDass ChayanDass Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes , i tried this , but it increses the api response time or any error may lead to panic situation

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, got it! Thanks for clarifying!


}

func LogError(msg string, fields ...zap.Field) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for having this function wrapper?
logger.LogError and logger.Error doesn't have much difference?

Copy link
Contributor Author

@ChayanDass ChayanDass Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need of wrapper, but this wrapper is useful if in future any need of change the structure of logs


if email.IsEmailServiceRunning() {

go email.Email.QueueBulkInsertEmail(email.BulkInsertJob{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to send mail to all admins on any update/create/import

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I keep all admins as cc / bcc? Or send separate emails

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think sending separate emails will be better as we can personalize content(if needed, in the future) or log per-recipient success/failure.

@ChayanDass ChayanDass force-pushed the mail2 branch 4 times, most recently from a3bab29 to 3015683 Compare October 29, 2025 14:25
@ChayanDass ChayanDass requested a review from deo002 October 31, 2025 10:59
@ChayanDass
Copy link
Contributor Author

i have added all the suggestions , and for email to admins, i have modify the wrapper function to add a another email to queue and added all the admins to "to", all the admins will get a personlized single email ,
templete have tp chnge according to creator and admin ,,,,

@deo002 , could you check the latest changes.....

Todo :

  1. create the templetes for admins and creator
  2. modift the get getadmins function according to need ...

username = s.from
}

d := gomail.NewDialer(s.host, s.port, username, s.pass)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to use a global instance of this variable which is initialised once and then used again and again?

logger *zap.Logger
wg sync.WaitGroup
mu sync.Mutex
running bool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I understand, running and closed variables are used to prevent double starts and prevent calling queue if service is not initialized.

We wont ever face the problem of double starts as Start is only called once in the Init function which is called once in main.

While calling Queue as well, we check whether the service is initialized via Email == nil.

Thus, running and closed are redundant and can be removed.

retryDelay time.Duration

// IsRunning cache
lastCheckMu sync.Mutex
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think processWithRetries should suffice and isRunning function is not needed. What do you suggest?

isRunning will save network calls to the smtp server in case it is unreachable(which it will rarely be) and will add one network to all other cases

}
}

func (s *AsyncEmailService) Queue(ctx context.Context, email EmailData) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename funtion to enqueue

return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we declare this context in the Queue function itself?

flag.Parse()

// Start the email service
EnableSMTP, _ := strconv.ParseBool(os.Getenv("ENABLE_SMTP"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename variable to enableSMTP

if err := db.DB.Where(&models.User{UserLevel: &level}).First(&user).Error; err != nil {
log.Fatalf("Failed to find a super admin")
}
var total, success, failed int
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variables declared but not initialized

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants