aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-03-07 18:42:40 +0530
committerBobby <[email protected]>2026-03-07 18:42:40 +0530
commit96c136f046d78c51210927e61483a36a220fedcb (patch)
tree8f5baf294d92ec3bc503bd02f4a08871874f048d /utils
parent6bc2ef7a8972547f10a5a8f50269b0e6b487a580 (diff)
downloaddove-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.go7
-rw-r--r--utils/email/functions.go79
-rw-r--r--utils/email/parse.go29
-rw-r--r--utils/email/types.go24
-rw-r--r--utils/meta/constants.go7
-rw-r--r--utils/meta/pagination.go84
-rw-r--r--utils/meta/types.go10
-rw-r--r--utils/smtp/functions.go34
-rw-r--r--utils/smtp/server.go27
-rw-r--r--utils/smtp/session.go15
-rw-r--r--utils/smtp/storage.go22
-rw-r--r--utils/smtp/types.go4
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
}