From caf265e7050edefa64ecf7e13828ec9636bce867 Mon Sep 17 00:00:00 2001 From: Bobby Date: Sun, 8 Mar 2026 03:06:23 +0530 Subject: Refactor configuration handling and add mail management features - Removed dependency on messages package in TOML loading and parsing. - Introduced new config constants and messages for better clarity and maintainability. - Implemented mail user and mailbox management with corresponding controllers and views. - Added new templates for mailboxes, mailbox creation, and user management. - Enhanced logging and error handling throughout the application. - Established a structured approach for applying default values in TOML configuration. - Created new utility functions for SMTP and email handling. --- services/auth.go | 37 ------------ services/auth/auth.go | 2 +- services/constants.go | 5 -- services/email.go | 31 ---------- services/functions.go | 137 --------------------------------------------- services/mail/mailboxes.go | 18 +++--- services/mail/users.go | 16 +++--- services/mailbox.go | 68 ---------------------- services/overview.go | 16 ------ services/user.go | 50 ----------------- 10 files changed, 18 insertions(+), 362 deletions(-) delete mode 100644 services/auth.go delete mode 100644 services/constants.go delete mode 100644 services/email.go delete mode 100644 services/functions.go delete mode 100644 services/mailbox.go delete mode 100644 services/overview.go delete mode 100644 services/user.go (limited to 'services') diff --git a/services/auth.go b/services/auth.go deleted file mode 100644 index ce62d10..0000000 --- a/services/auth.go +++ /dev/null @@ -1,37 +0,0 @@ -package services - -import ( - "dove/config" - "dove/enums" - "dove/messages" - "dove/types" - "dove/utils/auth" - "dove/utils/shortcuts" - - "github.com/gofiber/fiber/v2" -) - -func Authenticate(context *fiber.Ctx, request types.LoginRequest) (*types.MessageResponse, *types.ServiceError) { - switch request.Username == config.Server.Username && request.Password == config.Server.Password { - case true: - if sessionError := auth.Authenticate(context); sessionError != nil { - return nil, shortcuts.ServiceError(enums.Internal, sessionError.Error()) - } - - return &types.MessageResponse{ - Message: messages.AuthAuthenticated, - }, nil - default: - return nil, shortcuts.ServiceError(enums.Unauthorized, messages.AuthInvalidCredentials) - } -} - -func Deauthenticate(context *fiber.Ctx) (*types.MessageResponse, *types.ServiceError) { - if sessionError := auth.Deauthenticate(context); sessionError != nil { - return nil, shortcuts.ServiceError(enums.Internal, sessionError.Error()) - } - - return &types.MessageResponse{ - Message: messages.AuthLoggedOut, - }, nil -} \ No newline at end of file diff --git a/services/auth/auth.go b/services/auth/auth.go index 89ef205..b8a0e13 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -18,7 +18,7 @@ type MessageResponse struct { } func Authenticate(context *fiber.Ctx, request LoginRequest) (*MessageResponse, *shortcuts.Error) { - switch request.Username == config.Server.Username && request.Password == config.Server.Password { + switch request.Username == config.HTTP.Username && request.Password == config.HTTP.Password { case true: if sessionError := authUtils.Authenticate(context); sessionError != nil { return nil, shortcuts.ServiceError(shortcuts.Internal, sessionError.Error()) diff --git a/services/constants.go b/services/constants.go deleted file mode 100644 index bba619b..0000000 --- a/services/constants.go +++ /dev/null @@ -1,5 +0,0 @@ -package services - -const ( - LOG_PREFIX = "Services" -) diff --git a/services/email.go b/services/email.go deleted file mode 100644 index 36ae5f6..0000000 --- a/services/email.go +++ /dev/null @@ -1,31 +0,0 @@ -package services - -import ( - "dove/messages" - "dove/utils/email" - "dove/utils/logger" -) - -func ProcessEmail(rawMessage []byte, recipientAddresses []string) error { - parsedEmail, parseError := email.Parse(rawMessage) - if parseError != nil { - logger.Errorf(LOG_PREFIX, messages.EmailParseFailed, parseError) - return parseError - } - - mailboxes := ResolveMailboxes(recipientAddresses) - if len(mailboxes) == 0 { - return nil - } - - for _, mailbox := range mailboxes { - if storeError := storeEmailForMailbox(rawMessage, parsedEmail, mailbox); storeError != nil { - logger.Errorf(LOG_PREFIX, messages.EmailStoreFailed, storeError) - return storeError - } - } - - logger.Infof(LOG_PREFIX, messages.EmailProcessed, len(mailboxes)) - - return nil -} diff --git a/services/functions.go b/services/functions.go deleted file mode 100644 index ffba7f4..0000000 --- a/services/functions.go +++ /dev/null @@ -1,137 +0,0 @@ -package services - -import ( - "dove/config" - "dove/enums" - "dove/messages" - "dove/models" - "dove/repositories" - "dove/utils/email" - "dove/utils/logger" - "fmt" - "os" - "path/filepath" - "strings" - "time" -) - -func resolveMailbox(address string) *models.Mailbox { - mailbox := repositories.FindMailboxByAddress(address) - if mailbox != nil { - return mailbox - } - - alias := repositories.FindAliasByAddress(address) - if alias != nil { - return &alias.Mailbox - } - - switch config.Mailbox.Mode { - case enums.Catchall: - return createMailboxForAddress(address) - default: - logger.Warnf(LOG_PREFIX, messages.MailboxNotRegistered, address) - return nil - } -} - -func createMailboxForAddress(address string) *models.Mailbox { - user := repositories.FindUserByUsername(address) - if user == nil { - user = &models.User{ - Username: address, - DisplayName: address, - } - repositories.CreateUser(user) - } - - mailbox := &models.Mailbox{ - Address: address, - UserID: user.ID, - } - - repositories.CreateMailbox(mailbox) - logger.Infof(LOG_PREFIX, messages.MailboxAutoCreated, address) - - return mailbox -} - -func storeEmailForMailbox(rawMessage []byte, parsedEmail *email.ParsedEmail, mailbox models.Mailbox) error { - filename, saveError := saveEmailFile(rawMessage, mailbox.ID) - if saveError != nil { - return saveError - } - - attachmentCount, inlineCount := countAttachments(parsedEmail.Attachments) - - emailRecord := &models.Email{ - MailboxID: mailbox.ID, - MessageID: parsedEmail.MessageID, - Filename: filename, - FromAddress: parsedEmail.FromAddress, - FromName: parsedEmail.FromName, - ToAddresses: strings.Join(parsedEmail.ToAddresses, ", "), - CcAddresses: strings.Join(parsedEmail.CcAddresses, ", "), - BccAddresses: strings.Join(parsedEmail.BccAddresses, ", "), - ReplyToAddress: parsedEmail.ReplyToAddress, - ReturnPath: parsedEmail.ReturnPath, - Subject: parsedEmail.Subject, - Snippet: parsedEmail.Snippet, - Size: parsedEmail.Size, - AttachmentCount: attachmentCount, - InlineCount: inlineCount, - } - - if indexError := repositories.CreateEmail(emailRecord); indexError != nil { - logger.Errorf(LOG_PREFIX, messages.EmailIndexFailed, indexError) - return indexError - } - - for _, parsedAttachment := range parsedEmail.Attachments { - attachmentRecord := &models.Attachment{ - EmailID: emailRecord.ID, - Filename: parsedAttachment.Filename, - ContentType: parsedAttachment.ContentType, - ContentID: parsedAttachment.ContentID, - Size: parsedAttachment.Size, - IsInline: parsedAttachment.IsInline, - } - - repositories.CreateAttachment(attachmentRecord) - } - - return nil -} - -func saveEmailFile(rawMessage []byte, mailboxID uint) (string, error) { - mailboxDirectory := filepath.Join(config.DataDir, "emails", fmt.Sprintf("%d", mailboxID)) - - if directoryError := os.MkdirAll(mailboxDirectory, 0750); directoryError != nil { - return "", directoryError - } - - filename := fmt.Sprintf("%d.eml", time.Now().UnixNano()) - filePath := filepath.Join(mailboxDirectory, filename) - - if writeError := os.WriteFile(filePath, rawMessage, 0640); writeError != nil { - return "", writeError - } - - return filename, nil -} - -func countAttachments(attachments []email.ParsedAttachment) (int, int) { - attachmentCount := 0 - inlineCount := 0 - - for _, attachment := range attachments { - switch attachment.IsInline { - case true: - inlineCount++ - default: - attachmentCount++ - } - } - - return attachmentCount, inlineCount -} diff --git a/services/mail/mailboxes.go b/services/mail/mailboxes.go index b124dc9..89af4c4 100644 --- a/services/mail/mailboxes.go +++ b/services/mail/mailboxes.go @@ -3,8 +3,8 @@ package mail import ( "strings" - "dove/models" - "dove/repositories" + mailModel "dove/models/mail" + mailRepo "dove/repositories/mail" "dove/utils/meta" "dove/utils/shortcuts" ) @@ -15,7 +15,7 @@ type CreateMailboxRequest struct { } type MailboxFormResponse struct { - Users []models.User `json:"users"` + Users []mailModel.User `json:"users"` } type MailboxView struct { @@ -23,13 +23,13 @@ type MailboxView struct { } func ListMailboxes(pagination meta.Pagination, sorting meta.Sorting, search string) meta.PaginatedResponse { - mailboxes, total := repositories.ListMailboxes(pagination, sorting, search) + mailboxes, total := mailRepo.ListMailboxes(pagination, sorting, search) return pagination.Response(mailboxes, total) } func MailboxFormData() MailboxFormResponse { return MailboxFormResponse{ - Users: repositories.AllUsers(), + Users: mailRepo.AllUsers(), } } @@ -43,20 +43,20 @@ func CreateMailbox(request CreateMailboxRequest) *shortcuts.Error { return shortcuts.ServiceError(shortcuts.BadRequest, UserRequired) } - if repositories.FindUserByID(request.UserID) == nil { + if mailRepo.FindUserByID(request.UserID) == nil { return shortcuts.ServiceError(shortcuts.Unprocessable, UserNotFound) } - if repositories.FindMailboxByAddress(address) != nil { + if mailRepo.FindMailboxByAddress(address) != nil { return shortcuts.ServiceError(shortcuts.Unprocessable, AlreadyExists) } - mailbox := &models.Mailbox{ + mailbox := &mailModel.Mailbox{ Address: address, UserID: request.UserID, } - if createError := repositories.CreateMailbox(mailbox); createError != nil { + if createError := mailRepo.CreateMailbox(mailbox); createError != nil { return shortcuts.ServiceError(shortcuts.Internal, CreationFailed) } diff --git a/services/mail/users.go b/services/mail/users.go index 9520219..9c776a1 100644 --- a/services/mail/users.go +++ b/services/mail/users.go @@ -3,8 +3,8 @@ package mail import ( "strings" - "dove/models" - "dove/repositories" + mailModel "dove/models/mail" + mailRepo "dove/repositories/mail" "dove/utils/meta" "dove/utils/shortcuts" ) @@ -15,7 +15,7 @@ type CreateUserRequest struct { } func ListUsers(pagination meta.Pagination, sorting meta.Sorting, search string) meta.PaginatedResponse { - users, total := repositories.ListUsers(pagination, sorting, search) + users, total := mailRepo.ListUsers(pagination, sorting, search) return pagination.Response(users, total) } @@ -31,22 +31,22 @@ func CreateUser(request CreateUserRequest) *shortcuts.Error { return shortcuts.ServiceError(shortcuts.BadRequest, DisplayNameRequired) } - if repositories.FindUserByUsername(username) != nil { + if mailRepo.FindUserByUsername(username) != nil { return shortcuts.ServiceError(shortcuts.Unprocessable, UserAlreadyExists) } - newUser := &models.User{ + newUser := &mailModel.User{ Username: username, DisplayName: displayName, } - if createError := repositories.CreateUser(newUser); createError != nil { + if createError := mailRepo.CreateUser(newUser); createError != nil { return shortcuts.ServiceError(shortcuts.Internal, UserCreationFailed) } return nil } -func AllUsers() []models.User { - return repositories.AllUsers() +func AllUsers() []mailModel.User { + return mailRepo.AllUsers() } diff --git a/services/mailbox.go b/services/mailbox.go deleted file mode 100644 index d1e954d..0000000 --- a/services/mailbox.go +++ /dev/null @@ -1,68 +0,0 @@ -package services - -import ( - "strings" - - "dove/enums" - "dove/messages" - "dove/models" - "dove/repositories" - "dove/types" - "dove/utils/meta" - "dove/utils/shortcuts" -) - -func ListMailboxes(pagination meta.Pagination, sorting meta.Sorting, search string) types.PaginatedResponse { - mailboxes, total := repositories.ListMailboxes(pagination, sorting, search) - return pagination.Response(mailboxes, total) -} - -func MailboxFormData() types.MailboxFormResponse { - return types.MailboxFormResponse{ - Users: repositories.AllUsers(), - } -} - -func CreateMailbox(request types.CreateMailboxRequest) *types.ServiceError { - address := strings.TrimSpace(request.Address) - - if address == "" { - return shortcuts.ServiceError(enums.BadRequest, messages.MailboxAddressRequired) - } - - if request.UserID == 0 { - return shortcuts.ServiceError(enums.BadRequest, messages.MailboxUserRequired) - } - - if repositories.FindUserByID(request.UserID) == nil { - return shortcuts.ServiceError(enums.Unprocessable, messages.MailboxUserNotFound) - } - - if repositories.FindMailboxByAddress(address) != nil { - return shortcuts.ServiceError(enums.Unprocessable, messages.MailboxAlreadyExists) - } - - mailbox := &models.Mailbox{ - Address: address, - UserID: request.UserID, - } - - if createError := repositories.CreateMailbox(mailbox); createError != nil { - return shortcuts.ServiceError(enums.Internal, messages.MailboxCreationFailed) - } - - return nil -} - -func ResolveMailboxes(recipientAddresses []string) []models.Mailbox { - var resolvedMailboxes []models.Mailbox - - for _, address := range recipientAddresses { - mailbox := resolveMailbox(address) - if mailbox != nil { - resolvedMailboxes = append(resolvedMailboxes, *mailbox) - } - } - - return resolvedMailboxes -} diff --git a/services/overview.go b/services/overview.go deleted file mode 100644 index d0d3f60..0000000 --- a/services/overview.go +++ /dev/null @@ -1,16 +0,0 @@ -package services - -import ( - "dove/config" - "dove/repositories" - "dove/types" - "fmt" -) - -func Overview() types.Overview { - return types.Overview{ - MailboxCount: repositories.CountMailboxes(), - EmailCount: repositories.CountEmails(), - SMTPAddress: fmt.Sprintf(":%d", config.SMTP.Port), - } -} diff --git a/services/user.go b/services/user.go deleted file mode 100644 index b2d43cf..0000000 --- a/services/user.go +++ /dev/null @@ -1,50 +0,0 @@ -package services - -import ( - "strings" - - "dove/enums" - "dove/messages" - "dove/models" - "dove/repositories" - "dove/types" - "dove/utils/meta" - "dove/utils/shortcuts" -) - -func ListUsers(pagination meta.Pagination, sorting meta.Sorting, search string) types.PaginatedResponse { - users, total := repositories.ListUsers(pagination, sorting, search) - return pagination.Response(users, total) -} - -func CreateUser(request types.CreateUserRequest) *types.ServiceError { - username := strings.TrimSpace(request.Username) - displayName := strings.TrimSpace(request.DisplayName) - - if username == "" { - return shortcuts.ServiceError(enums.BadRequest, messages.UserUsernameRequired) - } - - if displayName == "" { - return shortcuts.ServiceError(enums.BadRequest, messages.UserDisplayNameRequired) - } - - if repositories.FindUserByUsername(username) != nil { - return shortcuts.ServiceError(enums.Unprocessable, messages.UserAlreadyExists) - } - - user := &models.User{ - Username: username, - DisplayName: displayName, - } - - if createError := repositories.CreateUser(user); createError != nil { - return shortcuts.ServiceError(enums.Internal, messages.UserCreationFailed) - } - - return nil -} - -func AllUsers() []models.User { - return repositories.AllUsers() -} -- cgit v1.2.3