diff options
| author | Bobby <[email protected]> | 2026-03-07 18:42:40 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2026-03-07 18:42:40 +0530 |
| commit | 96c136f046d78c51210927e61483a36a220fedcb (patch) | |
| tree | 8f5baf294d92ec3bc503bd02f4a08871874f048d /utils | |
| parent | 6bc2ef7a8972547f10a5a8f50269b0e6b487a580 (diff) | |
| download | dove-96c136f046d78c51210927e61483a36a220fedcb.tar.xz dove-96c136f046d78c51210927e61483a36a220fedcb.zip | |
Refactor dashboard and mailboxes pages to integrate services for data retrieval
- Updated `Dashboard` function to use `services.Overview()` for rendering overview data.
- Enhanced `Mailboxes` function to include pagination, sorting, and search functionality using `services.ListMailboxes()`.
- Modified `Users` function to implement pagination, sorting, and search with `services.ListUsers()`.
Revamped templates for mailboxes and users
- Updated `mailboxes.htmx.django` to display mailbox items dynamically with total count.
- Enhanced `users.htmx.django` to show user details and total count, with improved layout for user information.
Introduced new response types and constants
- Added `PaginatedResponse` type in `types/response.go` for consistent pagination responses.
- Introduced constants for pagination in `utils/meta/constants.go`.
Implemented email processing and storage services
- Created `services/email.go` for processing incoming emails and storing them in the database.
- Added email parsing utilities in `utils/email` for handling email content and attachments.
Established repository functions for mailboxes, users, and emails
- Created repository functions in `repositories` for managing mailboxes, users, and emails, including listing and searching capabilities.
Refactored SMTP server functions
- Updated SMTP server handling in `utils/smtp` to streamline session management and message processing.
- Removed obsolete storage functions and integrated email processing directly into the SMTP session.
Added new message constants for better logging and error handling
- Introduced message constants in `messages/email.go` and `messages/mailbox.go` for improved clarity in logs.
Diffstat (limited to 'utils')
| -rw-r--r-- | utils/email/constants.go | 7 | ||||
| -rw-r--r-- | utils/email/functions.go | 79 | ||||
| -rw-r--r-- | utils/email/parse.go | 29 | ||||
| -rw-r--r-- | utils/email/types.go | 24 | ||||
| -rw-r--r-- | utils/meta/constants.go | 7 | ||||
| -rw-r--r-- | utils/meta/pagination.go | 84 | ||||
| -rw-r--r-- | utils/meta/types.go | 10 | ||||
| -rw-r--r-- | utils/smtp/functions.go | 34 | ||||
| -rw-r--r-- | utils/smtp/server.go | 27 | ||||
| -rw-r--r-- | utils/smtp/session.go | 15 | ||||
| -rw-r--r-- | utils/smtp/storage.go | 22 | ||||
| -rw-r--r-- | utils/smtp/types.go | 4 |
12 files changed, 282 insertions, 60 deletions
diff --git a/utils/email/constants.go b/utils/email/constants.go new file mode 100644 index 0000000..4f3009d --- /dev/null +++ b/utils/email/constants.go @@ -0,0 +1,7 @@ +package email + +const ( + LOG_PREFIX = "Email" + SNIPPET_LENGTH = 200 + ADDRESS_JOINER = ", " +) diff --git a/utils/email/functions.go b/utils/email/functions.go new file mode 100644 index 0000000..b65a98c --- /dev/null +++ b/utils/email/functions.go @@ -0,0 +1,79 @@ +package email + +import ( + "net/mail" + "strings" + + "github.com/jhillyerd/enmime" +) + +func extractAddress(rawHeader string) string { + parsed, parseError := mail.ParseAddress(rawHeader) + if parseError != nil { + return rawHeader + } + + return parsed.Address +} + +func extractName(rawHeader string) string { + parsed, parseError := mail.ParseAddress(rawHeader) + if parseError != nil { + return "" + } + + return parsed.Name +} + +func extractAddressList(rawHeader string) []string { + if rawHeader == "" { + return nil + } + + addresses, parseError := mail.ParseAddressList(rawHeader) + if parseError != nil { + return []string{rawHeader} + } + + extracted := make([]string, len(addresses)) + for index, address := range addresses { + extracted[index] = address.Address + } + + return extracted +} + +func generateSnippet(textBody string) string { + trimmed := strings.TrimSpace(textBody) + if len(trimmed) <= SNIPPET_LENGTH { + return trimmed + } + + return trimmed[:SNIPPET_LENGTH] +} + +func extractAttachments(envelope *enmime.Envelope) []ParsedAttachment { + var attachments []ParsedAttachment + + for _, attachment := range envelope.Attachments { + attachments = append(attachments, ParsedAttachment{ + Filename: attachment.FileName, + ContentType: attachment.ContentType, + ContentID: attachment.ContentID, + Size: int64(len(attachment.Content)), + IsInline: false, + }) + } + + for _, inline := range envelope.Inlines { + attachments = append(attachments, ParsedAttachment{ + Filename: inline.FileName, + ContentType: inline.ContentType, + ContentID: inline.ContentID, + Size: int64(len(inline.Content)), + IsInline: true, + }) + } + + return attachments +} diff --git a/utils/email/parse.go b/utils/email/parse.go new file mode 100644 index 0000000..549ac2f --- /dev/null +++ b/utils/email/parse.go @@ -0,0 +1,29 @@ +package email + +import ( + "bytes" + + "github.com/jhillyerd/enmime" +) + +func Parse(rawMessage []byte) (*ParsedEmail, error) { + envelope, parseError := enmime.ReadEnvelope(bytes.NewReader(rawMessage)) + if parseError != nil { + return nil, parseError + } + + return &ParsedEmail{ + MessageID: envelope.GetHeader("Message-ID"), + FromAddress: extractAddress(envelope.GetHeader("From")), + FromName: extractName(envelope.GetHeader("From")), + ToAddresses: extractAddressList(envelope.GetHeader("To")), + CcAddresses: extractAddressList(envelope.GetHeader("Cc")), + BccAddresses: extractAddressList(envelope.GetHeader("Bcc")), + ReplyToAddress: envelope.GetHeader("Reply-To"), + ReturnPath: envelope.GetHeader("Return-Path"), + Subject: envelope.GetHeader("Subject"), + Snippet: generateSnippet(envelope.Text), + Size: int64(len(rawMessage)), + Attachments: extractAttachments(envelope), + }, nil +}
\ No newline at end of file diff --git a/utils/email/types.go b/utils/email/types.go new file mode 100644 index 0000000..497fad7 --- /dev/null +++ b/utils/email/types.go @@ -0,0 +1,24 @@ +package email + +type ParsedEmail struct { + MessageID string + FromAddress string + FromName string + ToAddresses []string + CcAddresses []string + BccAddresses []string + ReplyToAddress string + ReturnPath string + Subject string + Snippet string + Size int64 + Attachments []ParsedAttachment +} + +type ParsedAttachment struct { + Filename string + ContentType string + ContentID string + Size int64 + IsInline bool +}
\ No newline at end of file diff --git a/utils/meta/constants.go b/utils/meta/constants.go index 0138884..13a2fde 100644 --- a/utils/meta/constants.go +++ b/utils/meta/constants.go @@ -1,6 +1,9 @@ package meta const ( - LOG_PREFIX = "Meta" - REQUEST_KEY = "Request" + DEFAULT_PAGE = 1 + DEFAULT_PER_PAGE = 20 + LOG_PREFIX = "Meta" + MAX_PER_PAGE = 50 + REQUEST_KEY = "Request" ) diff --git a/utils/meta/pagination.go b/utils/meta/pagination.go new file mode 100644 index 0000000..9244294 --- /dev/null +++ b/utils/meta/pagination.go @@ -0,0 +1,84 @@ +package meta + +import ( + "dove/types" + "strconv" + + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" +) + +func Paginate(context *fiber.Ctx) Pagination { + requestData := Request(context) + + page := DEFAULT_PAGE + perPage := DEFAULT_PER_PAGE + + if requestData != nil { + if pageValue := requestData.Query("page"); pageValue != nil { + parsed, _ := strconv.Atoi(pageValue.String()) + if parsed >= DEFAULT_PAGE { + page = parsed + } + } + + if perPageValue := requestData.Query("per_page"); perPageValue != nil { + parsed, _ := strconv.Atoi(perPageValue.String()) + if parsed >= DEFAULT_PAGE && parsed <= MAX_PER_PAGE { + perPage = parsed + } + } + } + + return Pagination{Page: page, PerPage: perPage} +} + +func Sort(context *fiber.Ctx, allowedFields []string, fallbackField string) Sorting { + requestData := Request(context) + + field := fallbackField + direction := "desc" + + if requestData != nil { + if sortValue := requestData.Query("sort"); sortValue != nil { + for _, allowedField := range allowedFields { + if sortValue.String() == allowedField { + field = sortValue.String() + break + } + } + } + + if orderValue := requestData.Query("order"); orderValue != nil { + switch orderValue.String() { + case "asc", "desc": + direction = orderValue.String() + } + } + } + + return Sorting{Field: field, Direction: direction} +} + +func (self Pagination) Apply(query *gorm.DB) *gorm.DB { + return query.Offset((self.Page - 1) * self.PerPage).Limit(self.PerPage) +} + +func (self Sorting) Apply(query *gorm.DB) *gorm.DB { + return query.Order(self.Field + " " + self.Direction) +} + +func (self Pagination) Response(items any, total int64) types.PaginatedResponse { + totalPages := int(total) / self.PerPage + if int(total)%self.PerPage > 0 { + totalPages++ + } + + return types.PaginatedResponse{ + Items: items, + Total: total, + Page: self.Page, + PerPage: self.PerPage, + TotalPages: totalPages, + } +} diff --git a/utils/meta/types.go b/utils/meta/types.go index 3bc8a0e..c7b8e4d 100644 --- a/utils/meta/types.go +++ b/utils/meta/types.go @@ -14,3 +14,13 @@ type request struct { type value struct { data string } + +type Pagination struct { + Page int + PerPage int +} + +type Sorting struct { + Field string + Direction string +} diff --git a/utils/smtp/functions.go b/utils/smtp/functions.go new file mode 100644 index 0000000..ae9aeae --- /dev/null +++ b/utils/smtp/functions.go @@ -0,0 +1,34 @@ +package smtp + +import ( + "dove/config" + "dove/messages" + "dove/utils/logger" + "time" + + "github.com/emersion/go-smtp" +) + +func createServer(address string) *smtp.Server { + smtpServer := smtp.NewServer(smtp.BackendFunc(func(connection *smtp.Conn) (smtp.Session, error) { + logger.Debugf(LOG_PREFIX, messages.SMTPSessionStarted, connection.Hostname()) + return &session{}, nil + })) + + smtpServer.Addr = address + smtpServer.Domain = config.SMTP.Domain + smtpServer.ReadTimeout = time.Duration(config.SMTP.ReadTimeout) * time.Second + smtpServer.WriteTimeout = time.Duration(config.SMTP.WriteTimeout) * time.Second + smtpServer.MaxMessageBytes = int64(config.SMTP.MaxMessageSize) + smtpServer.AllowInsecureAuth = true + + return smtpServer +} + +func startListener(smtpServer *smtp.Server, label string, address string) { + logger.Successf(LOG_PREFIX, messages.SMTPServerStarting, label, address) + + if listenError := smtpServer.ListenAndServe(); listenError != nil { + logger.Fatalf(LOG_PREFIX, messages.SMTPListenFailed, label, listenError) + } +}
\ No newline at end of file diff --git a/utils/smtp/server.go b/utils/smtp/server.go index 0092c16..778dd3b 100644 --- a/utils/smtp/server.go +++ b/utils/smtp/server.go @@ -5,9 +5,6 @@ import ( "dove/messages" "dove/utils/logger" "fmt" - "time" - - gosmtp "github.com/emersion/go-smtp" ) var activeServers []serverInstance @@ -30,27 +27,3 @@ func Shutdown() { logger.Infof(LOG_PREFIX, messages.SMTPShutdownComplete) } - -func createServer(address string) *gosmtp.Server { - smtpServer := gosmtp.NewServer(gosmtp.BackendFunc(func(connection *gosmtp.Conn) (gosmtp.Session, error) { - logger.Debugf(LOG_PREFIX, messages.SMTPSessionStarted, connection.Hostname()) - return &session{}, nil - })) - - smtpServer.Addr = address - smtpServer.Domain = config.SMTP.Domain - smtpServer.ReadTimeout = time.Duration(config.SMTP.ReadTimeout) * time.Second - smtpServer.WriteTimeout = time.Duration(config.SMTP.WriteTimeout) * time.Second - smtpServer.MaxMessageBytes = int64(config.SMTP.MaxMessageSize) - smtpServer.AllowInsecureAuth = true - - return smtpServer -} - -func startListener(smtpServer *gosmtp.Server, label string, address string) { - logger.Successf(LOG_PREFIX, messages.SMTPServerStarting, label, address) - - if listenError := smtpServer.ListenAndServe(); listenError != nil { - logger.Fatalf(LOG_PREFIX, messages.SMTPListenFailed, label, listenError) - } -} diff --git a/utils/smtp/session.go b/utils/smtp/session.go index 029c257..fe1f5c4 100644 --- a/utils/smtp/session.go +++ b/utils/smtp/session.go @@ -3,11 +3,12 @@ package smtp import ( "dove/config" "dove/messages" - "dove/utils/logger" + "dove/services" "dove/utils/errors" + "dove/utils/logger" "io" - gosmtp "github.com/emersion/go-smtp" + "github.com/emersion/go-smtp" ) func (self *session) AuthPlain(username string, password string) error { @@ -23,13 +24,13 @@ func (self *session) AuthPlain(username string, password string) error { return nil } -func (self *session) Mail(senderAddress string, mailOptions *gosmtp.MailOptions) error { +func (self *session) Mail(senderAddress string, _ *smtp.MailOptions) error { logger.Debugf(LOG_PREFIX, messages.SMTPMailFrom, senderAddress) self.fromAddress = senderAddress return nil } -func (self *session) Rcpt(recipientAddress string, recipientOptions *gosmtp.RcptOptions) error { +func (self *session) Rcpt(recipientAddress string, _ *smtp.RcptOptions) error { logger.Debugf(LOG_PREFIX, messages.SMTPRecipient, recipientAddress) self.toAddresses = append(self.toAddresses, recipientAddress) return nil @@ -43,9 +44,9 @@ func (self *session) Data(messageReader io.Reader) error { logger.Infof(LOG_PREFIX, messages.SMTPMessageReceived, len(rawMessage)) - if storeError := storeMessage(self.fromAddress, self.toAddresses, rawMessage); storeError != nil { - logger.Errorf(LOG_PREFIX, messages.SMTPMessageStoreFailed, storeError) - return storeError + if processError := services.ProcessEmail(rawMessage, self.toAddresses); processError != nil { + logger.Errorf(LOG_PREFIX, messages.SMTPMessageStoreFailed, processError) + return processError } return nil diff --git a/utils/smtp/storage.go b/utils/smtp/storage.go deleted file mode 100644 index b580d67..0000000 --- a/utils/smtp/storage.go +++ /dev/null @@ -1,22 +0,0 @@ -package smtp - -import ( - "dove/config" - "fmt" - "os" - "path/filepath" - "time" -) - -func storeMessage(fromAddress string, toAddresses []string, rawMessage []byte) error { - emailDirectory := filepath.Join(config.DataDir, "emails") - - if directoryError := os.MkdirAll(emailDirectory, 0750); directoryError != nil { - return directoryError - } - - filename := fmt.Sprintf("%d.eml", time.Now().UnixNano()) - filePath := filepath.Join(emailDirectory, filename) - - return os.WriteFile(filePath, rawMessage, 0640) -}
\ No newline at end of file diff --git a/utils/smtp/types.go b/utils/smtp/types.go index 38c6504..aced45a 100644 --- a/utils/smtp/types.go +++ b/utils/smtp/types.go @@ -1,6 +1,6 @@ package smtp -import gosmtp "github.com/emersion/go-smtp" +import "github.com/emersion/go-smtp" type session struct { fromAddress string @@ -8,6 +8,6 @@ type session struct { } type serverInstance struct { - server *gosmtp.Server + server *smtp.Server label string } |
