aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-03-08 03:06:23 +0530
committerBobby <[email protected]>2026-03-08 03:06:23 +0530
commitcaf265e7050edefa64ecf7e13828ec9636bce867 (patch)
tree6bb8554dbb34695a74c2dca556bf512998cf62ab
parentcca905d35412f1549400fc3d1aca6dc704d8cae6 (diff)
downloaddove-caf265e7050edefa64ecf7e13828ec9636bce867.tar.xz
dove-caf265e7050edefa64ecf7e13828ec9636bce867.zip
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.
-rw-r--r--config/config.go169
-rw-r--r--config/constants.go12
-rw-r--r--config/defaults.go12
-rw-r--r--config/functions.go75
-rw-r--r--config/messages.go10
-rw-r--r--config/types.go68
-rw-r--r--config/validation.go61
-rw-r--r--controllers/auth/auth.go (renamed from controllers/auth.go)13
-rw-r--r--controllers/domain/domain.go (renamed from controllers/domain.go)6
-rw-r--r--controllers/mail/mail.go37
-rw-r--r--controllers/mailbox.go24
-rw-r--r--controllers/user.go24
-rw-r--r--database/migration.go16
-rw-r--r--database/resolve.go (renamed from database/functions.go)4
-rw-r--r--dove/main.go39
-rw-r--r--example.config.toml145
-rw-r--r--models/alias.go10
-rw-r--r--models/attachment.go14
-rw-r--r--models/email.go25
-rw-r--r--models/mailbox.go12
-rw-r--r--models/tag.go9
-rw-r--r--models/user.go10
-rw-r--r--pages/dashboard.go14
-rw-r--r--pages/dashboard/dashboard.go13
-rw-r--r--pages/domain/domain.go (renamed from pages/domain.go)4
-rw-r--r--pages/home/home.go (renamed from pages/home.go)2
-rw-r--r--pages/mail/mailbox.go (renamed from pages/mailbox.go)8
-rw-r--r--pages/mail/mailboxes.go (renamed from pages/mailboxes.go)8
-rw-r--r--pages/mail/users.go (renamed from pages/users.go)8
-rw-r--r--repositories/alias.go18
-rw-r--r--repositories/constants.go5
-rw-r--r--repositories/email.go50
-rw-r--r--repositories/mailbox.go46
-rw-r--r--repositories/user.go62
-rw-r--r--router/auth.go6
-rw-r--r--router/base.go4
-rw-r--r--router/dashboard.go12
-rw-r--r--router/domain.go16
-rw-r--r--router/mail.go20
-rw-r--r--router/router.go10
-rw-r--r--services/auth.go37
-rw-r--r--services/auth/auth.go2
-rw-r--r--services/constants.go5
-rw-r--r--services/email.go31
-rw-r--r--services/functions.go137
-rw-r--r--services/mail/mailboxes.go18
-rw-r--r--services/mail/users.go16
-rw-r--r--services/mailbox.go68
-rw-r--r--services/overview.go16
-rw-r--r--services/user.go50
-rw-r--r--session/constants.go10
-rw-r--r--session/defaults.go10
-rw-r--r--session/messages.go5
-rw-r--r--session/session.go9
-rw-r--r--tags/constants.go5
-rw-r--r--tags/defaults.go5
-rw-r--r--tags/messages.go11
-rw-r--r--tags/tags.go12
-rw-r--r--tags/types.go18
-rw-r--r--tags/url.go39
-rw-r--r--templates/dashboard/mailboxes.django5
-rw-r--r--templates/domains/htmx/domains.htmx.django2
-rw-r--r--templates/mail/htmx/mailbox.htmx.django (renamed from templates/dashboard/htmx/mailbox.htmx.django)0
-rw-r--r--templates/mail/htmx/mailboxes.htmx.django (renamed from templates/dashboard/htmx/mailboxes.htmx.django)4
-rw-r--r--templates/mail/htmx/newmailbox.htmx.django (renamed from templates/dashboard/htmx/newmailbox.htmx.django)4
-rw-r--r--templates/mail/htmx/newuser.htmx.django (renamed from templates/dashboard/htmx/newuser.htmx.django)4
-rw-r--r--templates/mail/htmx/users.htmx.django (renamed from templates/dashboard/htmx/users.htmx.django)2
-rw-r--r--templates/mail/mailbox.django (renamed from templates/dashboard/mailbox.django)2
-rw-r--r--templates/mail/mailboxes.django5
-rw-r--r--templates/mail/newmailbox.django (renamed from templates/dashboard/newmailbox.django)2
-rw-r--r--templates/mail/newuser.django (renamed from templates/dashboard/newuser.django)2
-rw-r--r--templates/mail/users.django (renamed from templates/dashboard/users.django)2
-rw-r--r--templates/partials/sidebar.django4
-rw-r--r--utils/auth/auth.go4
-rw-r--r--utils/auth/constants.go5
-rw-r--r--utils/auth/defaults.go5
-rw-r--r--utils/email/constants.go7
-rw-r--r--utils/email/defaults.go7
-rw-r--r--utils/email/functions.go79
-rw-r--r--utils/email/parse.go98
-rw-r--r--utils/email/types.go24
-rw-r--r--utils/logger/constants.go25
-rw-r--r--utils/logger/defaults.go25
-rw-r--r--utils/logger/format.go63
-rw-r--r--utils/logger/functions.go53
-rw-r--r--utils/logger/logger.go18
-rw-r--r--utils/logger/messages.go5
-rw-r--r--utils/logger/types.go11
-rw-r--r--utils/smtp/defaults.go (renamed from utils/smtp/constants.go)2
-rw-r--r--utils/smtp/functions.go34
-rw-r--r--utils/smtp/messages.go15
-rw-r--r--utils/smtp/server.go45
-rw-r--r--utils/smtp/session.go36
-rw-r--r--utils/smtp/types.go13
-rw-r--r--utils/toml/apply.go (renamed from utils/toml/defaults.go)0
-rw-r--r--utils/toml/load.go3
-rw-r--r--utils/toml/messages.go7
-rw-r--r--utils/toml/parse.go5
98 files changed, 832 insertions, 1400 deletions
diff --git a/config/config.go b/config/config.go
index 7465da7..a6326de 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1,16 +1,95 @@
package config
import (
- "dove/messages"
+ "os"
+ "path/filepath"
+ "strings"
+
"dove/utils/logger"
+ "dove/utils/toml"
)
+type Http struct {
+ Host string `toml:"host" default:"0.0.0.0"`
+ Port int `toml:"port" default:"8080"`
+ Debug bool `toml:"debug" default:"false"`
+ Username string `toml:"username"`
+ Password string `toml:"password"`
+}
+
+type Dns struct {
+ Host string `toml:"host" default:"127.0.0.1"`
+ Port int `toml:"port" default:"5053"`
+ DefaultTTL int `toml:"default_ttl" default:"300"`
+}
+
+type Smtp struct {
+ Host string `toml:"host" default:"0.0.0.0"`
+ Port int `toml:"port" default:"5025"`
+ SMTPSPort int `toml:"smtps_port" default:"5465"`
+ StartTLSPort int `toml:"starttls_port" default:"5587"`
+ Domain string `toml:"domain" default:"localhost"`
+ MaxMessageSize int `toml:"max_message_size" default:"26214400"`
+ ReadTimeout int `toml:"read_timeout" default:"30"`
+ WriteTimeout int `toml:"write_timeout" default:"30"`
+ AuthRequired bool `toml:"auth_required" default:"false"`
+ Username string `toml:"username"`
+ Password string `toml:"password"`
+ TLSEnabled bool `toml:"tls_enabled" default:"false"`
+ TLSCertPath string `toml:"tls_cert"`
+ TLSKeyPath string `toml:"tls_key"`
+ RelayEnabled bool `toml:"relay_enabled" default:"false"`
+ RelayHost string `toml:"relay_host"`
+ RelayPort int `toml:"relay_port" default:"587"`
+ RelayUsername string `toml:"relay_username"`
+ RelayPassword string `toml:"relay_password"`
+ RelayStartTLS bool `toml:"relay_starttls" default:"true"`
+}
+
+type Imap struct {
+ Host string `toml:"host" default:"0.0.0.0"`
+ Port int `toml:"port" default:"5143"`
+ IMAPSPort int `toml:"imaps_port" default:"5993"`
+ AuthRequired bool `toml:"auth_required" default:"false"`
+ Username string `toml:"username"`
+ Password string `toml:"password"`
+ TLSEnabled bool `toml:"tls_enabled" default:"false"`
+ TLSCertPath string `toml:"tls_cert"`
+ TLSKeyPath string `toml:"tls_key"`
+}
+
+type Pop3 struct {
+ Host string `toml:"host" default:"0.0.0.0"`
+ Port int `toml:"port" default:"5110"`
+ POP3SPort int `toml:"pop3s_port" default:"5995"`
+ AuthRequired bool `toml:"auth_required" default:"false"`
+ Username string `toml:"username"`
+ Password string `toml:"password"`
+ TLSEnabled bool `toml:"tls_enabled" default:"false"`
+ TLSCertPath string `toml:"tls_cert"`
+ TLSKeyPath string `toml:"tls_key"`
+}
+
+type Storage struct {
+ Host string `toml:"host" default:"0.0.0.0"`
+ Port int `toml:"port" default:"5900"`
+ DataDir string `toml:"data_dir" default:"storage"`
+ MaxObjectSize int64 `toml:"max_object_size" default:"536870912"`
+}
+
+type PortAssignment struct {
+ Service string
+ Host string
+ Port int
+}
+
var (
- IMAP imap
- Mailbox mailbox
- POP3 pop3
- Server server
- SMTP smtp
+ HTTP Http
+ DNS Dns
+ SMTP Smtp
+ IMAP Imap
+ POP3 Pop3
+ S3 Storage
)
var (
@@ -27,24 +106,90 @@ func init() {
configFilePath := resolveConfigFilePath(DevMode, osConfigDirectory)
if createError := ensureConfigFileExists(configFilePath); createError != nil {
- logger.Fatalf(LOG_PREFIX, messages.ConfigCreateFailed, createError)
+ logger.Fatalf(LogPrefix, ConfigCreateFailed, createError)
}
if loadError := loadConfigFile(configFilePath); loadError != nil {
- logger.Fatalf(LOG_PREFIX, messages.ConfigFileLoadFailed, loadError)
+ logger.Fatalf(LogPrefix, ConfigFileLoadFailed, loadError)
}
- for _, section := range []any{&Server, &SMTP, &IMAP, &POP3, &Mailbox} {
+ for _, section := range []any{&HTTP, &DNS, &SMTP, &IMAP, &POP3, &S3} {
if parseError := parseSection(section); parseError != nil {
- logger.Fatalf(LOG_PREFIX, messages.ConfigSectionParseFailed, parseError)
+ logger.Fatalf(LogPrefix, ConfigSectionFailed, parseError)
}
}
AuthEnabled = isAuthEnabled()
if portError := ValidatePorts(); portError != nil {
- logger.Fatalf(LOG_PREFIX, messages.ConfigPortValidFailed, portError)
+ logger.Fatalf(LogPrefix, ConfigPortValidFailed, portError)
}
- logger.Successf(LOG_PREFIX, messages.ConfigLoaded)
+ logger.Successf(LogPrefix, ConfigLoaded)
+}
+
+func isDevelopmentMode() bool {
+ executablePath, pathError := os.Executable()
+ if pathError != nil {
+ return false
+ }
+
+ return strings.Contains(executablePath, GoBuildPathIndicator) ||
+ strings.Contains(executablePath, GoBuildAltIndicator)
+}
+
+func resolveOSConfigDirectory() string {
+ configDirectory, directoryError := os.UserConfigDir()
+ if directoryError != nil {
+ return CurrentDirectory
+ }
+
+ return filepath.Join(configDirectory, ApplicationDirectory)
+}
+
+func resolveConfigFilePath(developmentMode bool, osConfigDirectory string) string {
+ switch developmentMode {
+ case true:
+ return filepath.Join(CurrentDirectory, ConfigFileName)
+ default:
+ return filepath.Join(osConfigDirectory, ConfigFileName)
+ }
+}
+
+func resolveDataDirectory(developmentMode bool, osConfigDirectory string) string {
+ switch developmentMode {
+ case true:
+ return CurrentDirectory
+ default:
+ return osConfigDirectory
+ }
+}
+
+func ensureConfigFileExists(configFilePath string) error {
+ if _, statError := os.Stat(configFilePath); statError == nil {
+ return nil
+ }
+
+ configDirectory := filepath.Dir(configFilePath)
+ if mkdirError := os.MkdirAll(configDirectory, ConfigDirectoryPermissions); mkdirError != nil {
+ return mkdirError
+ }
+
+ return os.WriteFile(configFilePath, defaultConfigContent, ConfigFilePermissions)
+}
+
+func loadConfigFile(configFilePath string) error {
+ if _, statError := os.Stat(configFilePath); statError != nil {
+ return nil
+ }
+
+ return toml.LoadFile(configFilePath)
+}
+
+func parseSection(target any) error {
+ return toml.Parse(target)
+}
+
+func isAuthEnabled() bool {
+ return HTTP.Username != "" && HTTP.Password != ""
}
diff --git a/config/constants.go b/config/constants.go
deleted file mode 100644
index dae1811..0000000
--- a/config/constants.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package config
-
-const (
- APPLICATION_DIRECTORY = "dove"
- CONFIG_DIRECTORY_PERMISSIONS = 0755
- CONFIG_FILE_NAME = "config.toml"
- CONFIG_FILE_PERMISSIONS = 0644
- CURRENT_DIRECTORY = "."
- GO_BUILD_ALT_INDICATOR = "go_build"
- GO_BUILD_PATH_INDICATOR = "go-build"
- LOG_PREFIX = "Config"
-)
diff --git a/config/defaults.go b/config/defaults.go
new file mode 100644
index 0000000..1962820
--- /dev/null
+++ b/config/defaults.go
@@ -0,0 +1,12 @@
+package config
+
+const (
+ ApplicationDirectory = "dove"
+ ConfigDirectoryPermissions = 0755
+ ConfigFileName = "config.toml"
+ ConfigFilePermissions = 0644
+ CurrentDirectory = "."
+ GoBuildAltIndicator = "go_build"
+ GoBuildPathIndicator = "go-build"
+ LogPrefix = "Config"
+)
diff --git a/config/functions.go b/config/functions.go
deleted file mode 100644
index c6ce601..0000000
--- a/config/functions.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package config
-
-import (
- "os"
- "path/filepath"
- "strings"
-
- "dove/utils/toml"
-)
-
-func isDevelopmentMode() bool {
- executablePath, pathError := os.Executable()
- if pathError != nil {
- return false
- }
-
- return strings.Contains(executablePath, GO_BUILD_PATH_INDICATOR) ||
- strings.Contains(executablePath, GO_BUILD_ALT_INDICATOR)
-}
-
-func resolveOSConfigDirectory() string {
- configDirectory, directoryError := os.UserConfigDir()
- if directoryError != nil {
- return CURRENT_DIRECTORY
- }
-
- return filepath.Join(configDirectory, APPLICATION_DIRECTORY)
-}
-
-func resolveConfigFilePath(developmentMode bool, osConfigDirectory string) string {
- switch developmentMode {
- case true:
- return filepath.Join(CURRENT_DIRECTORY, CONFIG_FILE_NAME)
- default:
- return filepath.Join(osConfigDirectory, CONFIG_FILE_NAME)
- }
-}
-
-func resolveDataDirectory(developmentMode bool, osConfigDirectory string) string {
- switch developmentMode {
- case true:
- return CURRENT_DIRECTORY
- default:
- return osConfigDirectory
- }
-}
-
-func ensureConfigFileExists(configFilePath string) error {
- if _, statError := os.Stat(configFilePath); statError == nil {
- return nil
- }
-
- configDirectory := filepath.Dir(configFilePath)
- if mkdirError := os.MkdirAll(configDirectory, CONFIG_DIRECTORY_PERMISSIONS); mkdirError != nil {
- return mkdirError
- }
-
- return os.WriteFile(configFilePath, defaultConfigContent, CONFIG_FILE_PERMISSIONS)
-}
-
-func loadConfigFile(configFilePath string) error {
- if _, statError := os.Stat(configFilePath); statError != nil {
- return nil
- }
-
- return toml.LoadFile(configFilePath)
-}
-
-func parseSection(target any) error {
- return toml.Parse(target)
-}
-
-func isAuthEnabled() bool {
- return Server.Username != "" && Server.Password != ""
-}
diff --git a/config/messages.go b/config/messages.go
new file mode 100644
index 0000000..6cd3d9b
--- /dev/null
+++ b/config/messages.go
@@ -0,0 +1,10 @@
+package config
+
+const (
+ ConfigCreateFailed = "Failed to create config file: %v"
+ ConfigFileLoadFailed = "Failed to load config: %v"
+ ConfigLoaded = "Configuration loaded successfully."
+ ConfigPortCollision = "Port collision: %s and %s both configured on %s."
+ ConfigPortValidFailed = "Port validation failed: %v"
+ ConfigSectionFailed = "Failed to parse config section: %v"
+)
diff --git a/config/types.go b/config/types.go
deleted file mode 100644
index 33d37f7..0000000
--- a/config/types.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package config
-
-import "dove/enums"
-
-type server struct {
- Host string `toml:"host" default:"0.0.0.0"`
- Port int `toml:"port" default:"8080"`
- Debug bool `toml:"debug" default:"false"`
- Username string `toml:"username"`
- Password string `toml:"password"`
-}
-
-type smtp struct {
- Host string `toml:"host" default:"0.0.0.0"`
- Port int `toml:"port" default:"5025"`
- SMTPSPort int `toml:"smtps_port" default:"5465"`
- StartTLSPort int `toml:"starttls_port" default:"5587"`
- Domain string `toml:"domain" default:"localhost"`
- MaxMessageSize int `toml:"max_message_size" default:"26214400"`
- ReadTimeout int `toml:"read_timeout" default:"30"`
- WriteTimeout int `toml:"write_timeout" default:"30"`
- AuthRequired bool `toml:"auth_required" default:"false"`
- Username string `toml:"username"`
- Password string `toml:"password"`
- TLSEnabled bool `toml:"tls_enabled" default:"false"`
- TLSCertPath string `toml:"tls_cert"`
- TLSKeyPath string `toml:"tls_key"`
- RelayEnabled bool `toml:"relay_enabled" default:"false"`
- RelayHost string `toml:"relay_host"`
- RelayPort int `toml:"relay_port" default:"587"`
- RelayUsername string `toml:"relay_username"`
- RelayPassword string `toml:"relay_password"`
- RelayStartTLS bool `toml:"relay_starttls" default:"true"`
-}
-
-type imap struct {
- Host string `toml:"host" default:"0.0.0.0"`
- Port int `toml:"port" default:"5143"`
- IMAPSPort int `toml:"imaps_port" default:"5993"`
- AuthRequired bool `toml:"auth_required" default:"false"`
- Username string `toml:"username"`
- Password string `toml:"password"`
- TLSEnabled bool `toml:"tls_enabled" default:"false"`
- TLSCertPath string `toml:"tls_cert"`
- TLSKeyPath string `toml:"tls_key"`
-}
-
-type pop3 struct {
- Host string `toml:"host" default:"0.0.0.0"`
- Port int `toml:"port" default:"5110"`
- POP3SPort int `toml:"pop3s_port" default:"5995"`
- AuthRequired bool `toml:"auth_required" default:"false"`
- Username string `toml:"username"`
- Password string `toml:"password"`
- TLSEnabled bool `toml:"tls_enabled" default:"false"`
- TLSCertPath string `toml:"tls_cert"`
- TLSKeyPath string `toml:"tls_key"`
-}
-
-type mailbox struct {
- Mode enums.MailboxMode `toml:"mode" default:"registered"`
-}
-
-type portAssignment struct {
- service string
- host string
- port int
-}
diff --git a/config/validation.go b/config/validation.go
index 46b85ac..bbdcd9b 100644
--- a/config/validation.go
+++ b/config/validation.go
@@ -3,7 +3,6 @@ package config
import (
"fmt"
- "dove/messages"
"dove/utils/errors"
)
@@ -12,74 +11,44 @@ func ValidatePorts() error {
occupiedPorts := make(map[string]string)
for _, assignment := range portAssignments {
- portKey := fmt.Sprintf("%s:%d", assignment.host, assignment.port)
+ portKey := fmt.Sprintf("%s:%d", assignment.Host, assignment.Port)
if existingService, occupied := occupiedPorts[portKey]; occupied {
- return errors.Error(messages.ConfigPortCollision, assignment.service, existingService, portKey)
+ return errors.Error(ConfigPortCollision, assignment.Service, existingService, portKey)
}
- occupiedPorts[portKey] = assignment.service
+ occupiedPorts[portKey] = assignment.Service
}
return nil
}
-func collectPortAssignments() []portAssignment {
- assignments := []portAssignment{
- {
- service: "HTTP",
- host: Server.Host,
- port: Server.Port,
- },
- {
- service: "SMTP",
- host: SMTP.Host,
- port: SMTP.Port,
- },
- {
- service: "IMAP",
- host: IMAP.Host,
- port: IMAP.Port,
- },
- {
- service: "POP3",
- host: POP3.Host,
- port: POP3.Port,
- },
+func collectPortAssignments() []PortAssignment {
+ assignments := []PortAssignment{
+ {Service: "HTTP", Host: HTTP.Host, Port: HTTP.Port},
+ {Service: "DNS", Host: DNS.Host, Port: DNS.Port},
+ {Service: "SMTP", Host: SMTP.Host, Port: SMTP.Port},
+ {Service: "IMAP", Host: IMAP.Host, Port: IMAP.Port},
+ {Service: "POP3", Host: POP3.Host, Port: POP3.Port},
+ {Service: "S3", Host: S3.Host, Port: S3.Port},
}
if SMTP.TLSEnabled {
assignments = append(assignments,
- portAssignment{
- service: "SMTPS",
- host: SMTP.Host,
- port: SMTP.SMTPSPort,
- },
- portAssignment{
- service: "SMTP STARTTLS",
- host: SMTP.Host,
- port: SMTP.StartTLSPort,
- },
+ PortAssignment{Service: "SMTPS", Host: SMTP.Host, Port: SMTP.SMTPSPort},
+ PortAssignment{Service: "SMTP STARTTLS", Host: SMTP.Host, Port: SMTP.StartTLSPort},
)
}
if IMAP.TLSEnabled {
assignments = append(assignments,
- portAssignment{
- service: "IMAPS",
- host: IMAP.Host,
- port: IMAP.IMAPSPort,
- },
+ PortAssignment{Service: "IMAPS", Host: IMAP.Host, Port: IMAP.IMAPSPort},
)
}
if POP3.TLSEnabled {
assignments = append(assignments,
- portAssignment{
- service: "POP3S",
- host: POP3.Host,
- port: POP3.POP3SPort,
- },
+ PortAssignment{Service: "POP3S", Host: POP3.Host, Port: POP3.POP3SPort},
)
}
diff --git a/controllers/auth.go b/controllers/auth/auth.go
index 444026a..8140ddd 100644
--- a/controllers/auth.go
+++ b/controllers/auth/auth.go
@@ -1,8 +1,7 @@
-package controllers
+package auth
import (
- "dove/services"
- "dove/types"
+ authService "dove/services/auth"
"dove/utils/meta"
"dove/utils/shortcuts"
@@ -10,12 +9,12 @@ import (
)
func Login(context *fiber.Ctx) error {
- body, parseError := meta.Body[types.LoginRequest](context)
+ body, parseError := meta.Body[authService.LoginRequest](context)
if parseError != nil {
- return shortcuts.BadRequest(context, parseError)
+ return shortcuts.BadRequestError(context, parseError)
}
- _, serviceError := services.Authenticate(context, body)
+ _, serviceError := authService.Authenticate(context, body)
if serviceError != nil {
return shortcuts.HandleError(context, serviceError)
}
@@ -24,7 +23,7 @@ func Login(context *fiber.Ctx) error {
}
func Logout(context *fiber.Ctx) error {
- _, serviceError := services.Deauthenticate(context)
+ _, serviceError := authService.Deauthenticate(context)
if serviceError != nil {
return shortcuts.HandleError(context, serviceError)
}
diff --git a/controllers/domain.go b/controllers/domain/domain.go
index 90a5727..1260e3d 100644
--- a/controllers/domain.go
+++ b/controllers/domain/domain.go
@@ -1,4 +1,4 @@
-package controllers
+package domain
import (
domainService "dove/services/domain"
@@ -11,7 +11,7 @@ import (
func CreateDomain(context *fiber.Ctx) error {
body, parseError := meta.Body[domainService.CreateDomainRequest](context)
if parseError != nil {
- return shortcuts.BadRequest(context, parseError)
+ return shortcuts.BadRequestError(context, parseError)
}
serviceError := domainService.CreateDomain(body)
@@ -25,7 +25,7 @@ func CreateDomain(context *fiber.Ctx) error {
func CreateTLD(context *fiber.Ctx) error {
body, parseError := meta.Body[domainService.CreateTLDRequest](context)
if parseError != nil {
- return shortcuts.BadRequest(context, parseError)
+ return shortcuts.BadRequestError(context, parseError)
}
serviceError := domainService.CreateTLD(body)
diff --git a/controllers/mail/mail.go b/controllers/mail/mail.go
new file mode 100644
index 0000000..976076b
--- /dev/null
+++ b/controllers/mail/mail.go
@@ -0,0 +1,37 @@
+package mail
+
+import (
+ mailService "dove/services/mail"
+ "dove/utils/meta"
+ "dove/utils/shortcuts"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+func CreateUser(context *fiber.Ctx) error {
+ body, parseError := meta.Body[mailService.CreateUserRequest](context)
+ if parseError != nil {
+ return shortcuts.BadRequestError(context, parseError)
+ }
+
+ serviceError := mailService.CreateUser(body)
+ if serviceError != nil {
+ return shortcuts.HandleError(context, serviceError)
+ }
+
+ return shortcuts.Redirect(context, "dashboard.users")
+}
+
+func CreateMailbox(context *fiber.Ctx) error {
+ body, parseError := meta.Body[mailService.CreateMailboxRequest](context)
+ if parseError != nil {
+ return shortcuts.BadRequestError(context, parseError)
+ }
+
+ serviceError := mailService.CreateMailbox(body)
+ if serviceError != nil {
+ return shortcuts.HandleError(context, serviceError)
+ }
+
+ return shortcuts.Redirect(context, "dashboard.mailboxes")
+} \ No newline at end of file
diff --git a/controllers/mailbox.go b/controllers/mailbox.go
deleted file mode 100644
index 0878bd3..0000000
--- a/controllers/mailbox.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package controllers
-
-import (
- "dove/services"
- "dove/types"
- "dove/utils/meta"
- "dove/utils/shortcuts"
-
- "github.com/gofiber/fiber/v2"
-)
-
-func CreateMailbox(context *fiber.Ctx) error {
- body, parseError := meta.Body[types.CreateMailboxRequest](context)
- if parseError != nil {
- return shortcuts.BadRequest(context, parseError)
- }
-
- serviceError := services.CreateMailbox(body)
- if serviceError != nil {
- return shortcuts.HandleError(context, serviceError)
- }
-
- return shortcuts.Redirect(context, "dashboard.mailboxes")
-} \ No newline at end of file
diff --git a/controllers/user.go b/controllers/user.go
deleted file mode 100644
index ea2008b..0000000
--- a/controllers/user.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package controllers
-
-import (
- "dove/services"
- "dove/types"
- "dove/utils/meta"
- "dove/utils/shortcuts"
-
- "github.com/gofiber/fiber/v2"
-)
-
-func CreateUser(context *fiber.Ctx) error {
- body, parseError := meta.Body[types.CreateUserRequest](context)
- if parseError != nil {
- return shortcuts.BadRequest(context, parseError)
- }
-
- serviceError := services.CreateUser(body)
- if serviceError != nil {
- return shortcuts.HandleError(context, serviceError)
- }
-
- return shortcuts.Redirect(context, "dashboard.users")
-} \ No newline at end of file
diff --git a/database/migration.go b/database/migration.go
index e18f2d6..efd3aba 100644
--- a/database/migration.go
+++ b/database/migration.go
@@ -1,8 +1,8 @@
package database
import (
- "dove/models"
"dove/models/domain"
+ "dove/models/mail"
"dove/utils/logger"
)
@@ -10,15 +10,15 @@ func migrate() {
migrationError := DB.AutoMigrate(
&domain.TLD{},
&domain.Domain{},
- &models.User{},
- &models.Mailbox{},
- &models.Alias{},
- &models.Email{},
- &models.Tag{},
- &models.Attachment{},
+ &mail.User{},
+ &mail.Mailbox{},
+ &mail.Alias{},
+ &mail.Email{},
+ &mail.Tag{},
+ &mail.Attachment{},
)
if migrationError != nil {
logger.Fatalf(LogPrefix, MigrationFailed, migrationError)
}
-}
+} \ No newline at end of file
diff --git a/database/functions.go b/database/resolve.go
index 09d6ae4..db90dc5 100644
--- a/database/functions.go
+++ b/database/resolve.go
@@ -13,10 +13,10 @@ func resolveDatabasePath() string {
}
func resolveGORMLogLevel() logger.Interface {
- switch config.Server.Debug {
+ switch config.HTTP.Debug {
case true:
return logger.Default.LogMode(logger.Info)
default:
return logger.Default.LogMode(logger.Silent)
}
-}
+} \ No newline at end of file
diff --git a/dove/main.go b/dove/main.go
index 4de7ef8..d03fd0a 100644
--- a/dove/main.go
+++ b/dove/main.go
@@ -7,34 +7,30 @@ import (
"syscall"
"dove/config"
- "dove/messages"
"dove/middleware"
"dove/router"
"dove/tags"
"dove/utils/logger"
"dove/utils/smtp"
+ _ "dove/repositories/init"
+
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/template/django/v3"
- "github.com/spf13/cobra"
)
-const LOG_PREFIX = "Server"
+const LogPrefix = "Server"
-var rootCommand = &cobra.Command{
- Use: "dove",
- Short: "Local SMTP email testing tool.",
- RunE: serve,
-}
+const (
+ ServerStarting = "Server started on %s."
+ ServerListenFailed = "Failed to start server: %v"
+ ServerShuttingDown = "Shutting down gracefully..."
+ ServerShutdownFailed = "Error during server shutdown: %v"
+ ServerShutdownComplete = "Shutdown complete."
+)
func main() {
- if executeError := rootCommand.Execute(); executeError != nil {
- logger.Fatalf(LOG_PREFIX, "%v", executeError)
- }
-}
-
-func serve(command *cobra.Command, arguments []string) error {
tags.Initialize()
engine := django.New("./templates", ".django")
@@ -57,23 +53,22 @@ func serve(command *cobra.Command, arguments []string) error {
signal.Notify(shutdownSignal, syscall.SIGINT, syscall.SIGTERM)
go func() {
- address := fmt.Sprintf("%s:%d", config.Server.Host, config.Server.Port)
- logger.Successf(LOG_PREFIX, messages.ServerStarting, address)
+ address := fmt.Sprintf("%s:%d", config.HTTP.Host, config.HTTP.Port)
+ logger.Successf(LogPrefix, ServerStarting, address)
if listenError := application.Listen(address); listenError != nil {
- logger.Fatalf(LOG_PREFIX, messages.ServerListenFailed, listenError)
+ logger.Fatalf(LogPrefix, ServerListenFailed, listenError)
}
}()
<-shutdownSignal
- logger.Infof(LOG_PREFIX, messages.ServerShuttingDown)
+ logger.Infof(LogPrefix, ServerShuttingDown)
smtp.Shutdown()
if shutdownError := application.Shutdown(); shutdownError != nil {
- logger.Errorf(LOG_PREFIX, messages.ServerShutdownFailed, shutdownError)
+ logger.Errorf(LogPrefix, ServerShutdownFailed, shutdownError)
}
- logger.Successf(LOG_PREFIX, messages.ServerShutdownComplete)
- return nil
-}
+ logger.Successf(LogPrefix, ServerShutdownComplete)
+} \ No newline at end of file
diff --git a/example.config.toml b/example.config.toml
index be3cdaf..962bea1 100644
--- a/example.config.toml
+++ b/example.config.toml
@@ -1,34 +1,66 @@
# =============================================================================
-# Dove - Local SMTP Email Testing Tool
+# Dove — Local Infrastructure Service for Peaceful Development Experience
# =============================================================================
+#
+# This is the default configuration file for Dove. It controls how each
+# service binds to the network and behaves at startup. Settings that can be
+# managed at runtime (queue policies, health check intervals, cron schedules,
+# KV limits, etc.) are configured through the dashboard and stored in the
+# database — they do not appear here.
+#
# Copy this file to config.toml and adjust values as needed.
# All values shown below are defaults and can be omitted if unchanged.
+# Commented-out values are optional and disabled by default.
-# -----------------------------------------------------------------------------
+# =============================================================================
# HTTP Server
-# -----------------------------------------------------------------------------
-# The web dashboard for viewing and managing captured emails.
+# =============================================================================
+# The web dashboard and REST API for managing your local infrastructure.
+# This is the primary interface for interacting with Dove.
-[server]
+[http]
# Network interface to bind the HTTP server to.
# Use "0.0.0.0" to listen on all interfaces, or "127.0.0.1" for local only.
host = "0.0.0.0"
-# Port for the web dashboard.
+# Port for the web dashboard and API.
port = 8080
-# Enable verbose debug logging for HTTP requests.
+# Enable verbose debug logging for HTTP requests and responses.
debug = false
# Optional credentials to protect the web dashboard.
-# Leave commented out to disable dashboard authentication.
+# When set, users must authenticate before accessing the dashboard.
+# Leave commented out to allow unrestricted access.
# username = ""
# password = ""
-# -----------------------------------------------------------------------------
+# =============================================================================
+# DNS Server
+# =============================================================================
+# Resolves custom TLDs (.dove, .local, .test, .nest) and all registered
+# domains. A records with port mappings act as a built-in reverse proxy,
+# routing domain requests to local services automatically.
+
+[dns]
+# Network interface to bind the DNS server to.
+# Typically bound to localhost since it serves local resolution only.
+host = "127.0.0.1"
+
+# Port for DNS queries over UDP.
+# The standard DNS port is 53, but a non-privileged port is used by default
+# to avoid requiring root permissions.
+port = 5053
+
+# Default time-to-live in seconds for DNS records when not explicitly set.
+default_ttl = 300
+
+# =============================================================================
# SMTP Server
-# -----------------------------------------------------------------------------
-# Receives incoming emails from mail clients and applications.
+# =============================================================================
+# Receives incoming emails for mailboxes registered under your domains.
+# Emails sent to non-existent mailboxes on existing domains will bounce.
+# Emails sent to non-existent domains are silently dropped.
[smtp]
# Network interface to bind the SMTP server to.
@@ -37,17 +69,19 @@ host = "0.0.0.0"
# Port for plain SMTP connections.
port = 5025
-# Port for implicit TLS (SMTPS) connections. Requires tls_enabled = true.
+# Port for implicit TLS (SMTPS) connections.
+# Only active when tls_enabled is set to true.
smtps_port = 5465
-# Port for STARTTLS connections. Requires tls_enabled = true.
+# Port for STARTTLS upgrade connections.
+# Only active when tls_enabled is set to true.
starttls_port = 5587
# The domain name announced in SMTP EHLO/HELO greetings.
# This identifies the mail server to connecting clients.
domain = "localhost"
-# Maximum allowed email size in bytes (default: 25 MB).
+# Maximum allowed email size in bytes. Default is 25 MB.
max_message_size = 26214400
# Timeout in seconds for reading data from SMTP clients.
@@ -57,21 +91,25 @@ read_timeout = 30
write_timeout = 30
# Require SMTP authentication before accepting messages.
+# When enabled, clients must authenticate with the credentials below.
auth_required = false
-# Credentials for SMTP authentication. Only used when auth_required = true.
+# Credentials for SMTP authentication.
+# Only used when auth_required is set to true.
# username = ""
# password = ""
-# Enable TLS support for SMTPS and STARTTLS listeners.
+# Enable TLS support for encrypted SMTPS and STARTTLS listeners.
+# Requires valid certificate and key paths below.
tls_enabled = false
-# Paths to TLS certificate and private key files. Required when tls_enabled = true.
+# Paths to the TLS certificate and private key files.
+# Required when tls_enabled is set to true.
# tls_cert = ""
# tls_key = ""
-# Relay captured emails to an upstream SMTP server.
-# Useful for forwarding test emails to a real mail server.
+# Relay configuration for forwarding emails to an upstream SMTP server.
+# Useful for testing email delivery through a real mail provider.
relay_enabled = false
# relay_host = ""
relay_port = 587
@@ -79,10 +117,11 @@ relay_port = 587
# relay_password = ""
relay_starttls = true
-# -----------------------------------------------------------------------------
+# =============================================================================
# IMAP Server
-# -----------------------------------------------------------------------------
-# Allows mail clients to retrieve captured emails via the IMAP protocol.
+# =============================================================================
+# Provides IMAP access for external mail clients (Thunderbird, Apple Mail,
+# etc.) to retrieve emails stored in Dove mailboxes.
[imap]
# Network interface to bind the IMAP server to.
@@ -91,27 +130,32 @@ host = "0.0.0.0"
# Port for plain IMAP connections.
port = 5143
-# Port for implicit TLS (IMAPS) connections. Requires tls_enabled = true.
+# Port for implicit TLS (IMAPS) connections.
+# Only active when tls_enabled is set to true.
imaps_port = 5993
-# Require IMAP authentication before granting access.
+# Require IMAP authentication before granting mailbox access.
auth_required = false
-# Credentials for IMAP authentication. Only used when auth_required = true.
+# Credentials for IMAP authentication.
+# Only used when auth_required is set to true.
# username = ""
# password = ""
-# Enable TLS support for the IMAPS listener.
+# Enable TLS support for encrypted IMAPS connections.
+# Requires valid certificate and key paths below.
tls_enabled = false
-# Paths to TLS certificate and private key files. Required when tls_enabled = true.
+# Paths to the TLS certificate and private key files.
+# Required when tls_enabled is set to true.
# tls_cert = ""
# tls_key = ""
-# -----------------------------------------------------------------------------
+# =============================================================================
# POP3 Server
-# -----------------------------------------------------------------------------
-# Allows mail clients to retrieve captured emails via the POP3 protocol.
+# =============================================================================
+# Provides POP3 access for external mail clients to download and remove
+# emails from Dove mailboxes.
[pop3]
# Network interface to bind the POP3 server to.
@@ -120,30 +164,43 @@ host = "0.0.0.0"
# Port for plain POP3 connections.
port = 5110
-# Port for implicit TLS (POP3S) connections. Requires tls_enabled = true.
+# Port for implicit TLS (POP3S) connections.
+# Only active when tls_enabled is set to true.
pop3s_port = 5995
-# Require POP3 authentication before granting access.
+# Require POP3 authentication before granting mailbox access.
auth_required = false
-# Credentials for POP3 authentication. Only used when auth_required = true.
+# Credentials for POP3 authentication.
+# Only used when auth_required is set to true.
# username = ""
# password = ""
-# Enable TLS support for the POP3S listener.
+# Enable TLS support for encrypted POP3S connections.
+# Requires valid certificate and key paths below.
tls_enabled = false
-# Paths to TLS certificate and private key files. Required when tls_enabled = true.
+# Paths to the TLS certificate and private key files.
+# Required when tls_enabled is set to true.
# tls_cert = ""
# tls_key = ""
-# -----------------------------------------------------------------------------
-# Mailbox
-# -----------------------------------------------------------------------------
-# Controls how incoming emails are routed to mailboxes.
+# =============================================================================
+# S3-Compatible Object Storage
+# =============================================================================
+# Filesystem-backed object storage with full S3 API compatibility.
+# Works with aws-sdk, boto3, minio-go, and any S3-compatible client.
+
+[storage]
+# Network interface to bind the S3 API server to.
+host = "0.0.0.0"
+
+# Port for S3 API requests.
+port = 5900
+
+# Directory where object data is stored on disk.
+# Relative paths are resolved from Dove's data directory.
+data_dir = "storage"
-[mailbox]
-# Mailbox routing mode:
-# "registered" - Only accept emails for registered mailbox addresses.
-# "open" - Automatically create mailboxes for any recipient address.
-mode = "registered"
+# Maximum allowed object size in bytes. Default is 512 MB.
+max_object_size = 536870912
diff --git a/models/alias.go b/models/alias.go
deleted file mode 100644
index 5b0dcb3..0000000
--- a/models/alias.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package models
-
-import "gorm.io/gorm"
-
-type Alias struct {
- gorm.Model
- SourceAddress string `gorm:"uniqueIndex;not null" json:"source_address"`
- MailboxID uint `gorm:"not null" json:"mailbox_id"`
- Mailbox Mailbox `gorm:"foreignKey:MailboxID" json:"mailbox"`
-}
diff --git a/models/attachment.go b/models/attachment.go
deleted file mode 100644
index cd1a741..0000000
--- a/models/attachment.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package models
-
-import "gorm.io/gorm"
-
-type Attachment struct {
- gorm.Model
- EmailID uint `gorm:"not null;index" json:"email_id"`
- Email Email `gorm:"foreignKey:EmailID" json:"email"`
- Filename string `gorm:"not null" json:"filename"`
- ContentType string `gorm:"not null" json:"content_type"`
- ContentID string `json:"content_id"`
- Size int64 `json:"size"`
- IsInline bool `gorm:"default:false" json:"is_inline"`
-}
diff --git a/models/email.go b/models/email.go
deleted file mode 100644
index 12b3ad1..0000000
--- a/models/email.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package models
-
-import "gorm.io/gorm"
-
-type Email struct {
- gorm.Model
- MailboxID uint `gorm:"not null;index" json:"mailbox_id"`
- Mailbox Mailbox `gorm:"foreignKey:MailboxID" json:"mailbox"`
- MessageID string `gorm:"index" json:"message_id"`
- Filename string `gorm:"uniqueIndex;not null" json:"filename"`
- FromAddress string `gorm:"not null" json:"from_address"`
- FromName string `json:"from_name"`
- ToAddresses string `gorm:"not null" json:"to_addresses"`
- CcAddresses string `json:"cc_addresses"`
- BccAddresses string `json:"bcc_addresses"`
- ReplyToAddress string `json:"reply_to_address"`
- ReturnPath string `json:"return_path"`
- Subject string `json:"subject"`
- Snippet string `json:"snippet"`
- Size int64 `json:"size"`
- IsRead bool `gorm:"default:false;index" json:"is_read"`
- AttachmentCount int `gorm:"default:0" json:"attachment_count"`
- InlineCount int `gorm:"default:0" json:"inline_count"`
- Tags []Tag `gorm:"many2many:email_tags" json:"tags"`
-}
diff --git a/models/mailbox.go b/models/mailbox.go
deleted file mode 100644
index da4114f..0000000
--- a/models/mailbox.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package models
-
-import "gorm.io/gorm"
-
-type Mailbox struct {
- gorm.Model
- Address string `gorm:"uniqueIndex;not null" json:"address"`
- UserID uint `gorm:"not null" json:"user_id"`
- User User `gorm:"foreignKey:UserID" json:"user"`
- Aliases []Alias `gorm:"foreignKey:MailboxID" json:"aliases"`
- Emails []Email `gorm:"foreignKey:MailboxID" json:"emails"`
-}
diff --git a/models/tag.go b/models/tag.go
deleted file mode 100644
index 79fa65f..0000000
--- a/models/tag.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package models
-
-import "gorm.io/gorm"
-
-type Tag struct {
- gorm.Model
- Name string `gorm:"uniqueIndex;not null" json:"name"`
- Emails []Email `gorm:"many2many:email_tags" json:"emails"`
-}
diff --git a/models/user.go b/models/user.go
deleted file mode 100644
index 6bb6edf..0000000
--- a/models/user.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package models
-
-import "gorm.io/gorm"
-
-type User struct {
- gorm.Model
- Username string `gorm:"uniqueIndex;not null" json:"username"`
- DisplayName string `json:"display_name"`
- Mailboxes []Mailbox `gorm:"foreignKey:UserID" json:"mailboxes"`
-}
diff --git a/pages/dashboard.go b/pages/dashboard.go
deleted file mode 100644
index 3fb16fb..0000000
--- a/pages/dashboard.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package pages
-
-import (
- "dove/services"
- "dove/utils/meta"
- "dove/utils/shortcuts"
-
- "github.com/gofiber/fiber/v2"
-)
-
-func Dashboard(context *fiber.Ctx) error {
- meta.SetPageTitle(context, "Overview")
- return shortcuts.Render(context, "dashboard/overview", services.Overview())
-}
diff --git a/pages/dashboard/dashboard.go b/pages/dashboard/dashboard.go
new file mode 100644
index 0000000..319f884
--- /dev/null
+++ b/pages/dashboard/dashboard.go
@@ -0,0 +1,13 @@
+package dashboard
+
+import (
+ "dove/utils/meta"
+ "dove/utils/shortcuts"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+func Overview(context *fiber.Ctx) error {
+ meta.SetPageTitle(context, "Overview")
+ return shortcuts.Render(context, "dashboard/overview", nil)
+}
diff --git a/pages/domain.go b/pages/domain/domain.go
index d46af9c..3d01df0 100644
--- a/pages/domain.go
+++ b/pages/domain/domain.go
@@ -1,4 +1,4 @@
-package pages
+package domain
import (
domainService "dove/services/domain"
@@ -21,4 +21,4 @@ func NewDomain(context *fiber.Ctx) error {
func NewTLD(context *fiber.Ctx) error {
meta.SetPageTitle(context, "New TLD")
return shortcuts.Render(context, "domains/newtld", nil)
-} \ No newline at end of file
+}
diff --git a/pages/home.go b/pages/home/home.go
index 06f836e..6107da1 100644
--- a/pages/home.go
+++ b/pages/home/home.go
@@ -1,4 +1,4 @@
-package pages
+package home
import (
"dove/config"
diff --git a/pages/mailbox.go b/pages/mail/mailbox.go
index a444a73..81f525b 100644
--- a/pages/mailbox.go
+++ b/pages/mail/mailbox.go
@@ -1,7 +1,7 @@
-package pages
+package mail
import (
- "dove/types"
+ mailService "dove/services/mail"
"dove/utils/meta"
"dove/utils/shortcuts"
@@ -9,9 +9,9 @@ import (
)
func Mailbox(context *fiber.Ctx) error {
- address := meta.Request(context).Param("address").Required()
+ address := meta.Request(context).Param("address")
meta.SetPageTitle(context, address)
- return shortcuts.Render(context, "dashboard/mailbox", types.Mailbox{
+ return shortcuts.Render(context, "mail/mailbox", mailService.MailboxView{
Address: address,
})
}
diff --git a/pages/mailboxes.go b/pages/mail/mailboxes.go
index 611c2cb..36d9041 100644
--- a/pages/mailboxes.go
+++ b/pages/mail/mailboxes.go
@@ -1,7 +1,7 @@
-package pages
+package mail
import (
- "dove/services"
+ mailService "dove/services/mail"
"dove/utils/meta"
"dove/utils/shortcuts"
@@ -10,7 +10,7 @@ import (
func NewMailbox(context *fiber.Ctx) error {
meta.SetPageTitle(context, "New Mailbox")
- return shortcuts.Render(context, "dashboard/newmailbox", services.MailboxFormData())
+ return shortcuts.Render(context, "mail/newmailbox", mailService.MailboxFormData())
}
func Mailboxes(context *fiber.Ctx) error {
@@ -20,5 +20,5 @@ func Mailboxes(context *fiber.Ctx) error {
sorting := meta.Sort(context, []string{"address", "created_at"}, "created_at")
search := context.Query("search")
- return shortcuts.Render(context, "dashboard/mailboxes", services.ListMailboxes(pagination, sorting, search))
+ return shortcuts.Render(context, "mail/mailboxes", mailService.ListMailboxes(pagination, sorting, search))
}
diff --git a/pages/users.go b/pages/mail/users.go
index 1fc6fcc..8700c80 100644
--- a/pages/users.go
+++ b/pages/mail/users.go
@@ -1,7 +1,7 @@
-package pages
+package mail
import (
- "dove/services"
+ mailService "dove/services/mail"
"dove/utils/meta"
"dove/utils/shortcuts"
@@ -10,7 +10,7 @@ import (
func NewUser(context *fiber.Ctx) error {
meta.SetPageTitle(context, "New User")
- return shortcuts.Render(context, "dashboard/newuser", nil)
+ return shortcuts.Render(context, "mail/newuser", nil)
}
func Users(context *fiber.Ctx) error {
@@ -20,5 +20,5 @@ func Users(context *fiber.Ctx) error {
sorting := meta.Sort(context, []string{"username", "display_name", "created_at"}, "created_at")
search := context.Query("search")
- return shortcuts.Render(context, "dashboard/users", services.ListUsers(pagination, sorting, search))
+ return shortcuts.Render(context, "mail/users", mailService.ListUsers(pagination, sorting, search))
}
diff --git a/repositories/alias.go b/repositories/alias.go
deleted file mode 100644
index ee038b2..0000000
--- a/repositories/alias.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package repositories
-
-import (
- "dove/database"
- "dove/models"
-
- "gorm.io/gorm"
-)
-
-func FindAliasByAddress(address string) *models.Alias {
- var alias models.Alias
- result := database.DB.Preload("Mailbox").Where("source_address = ?", address).First(&alias)
- if result.Error == gorm.ErrRecordNotFound {
- return nil
- }
-
- return &alias
-}
diff --git a/repositories/constants.go b/repositories/constants.go
deleted file mode 100644
index 29ce495..0000000
--- a/repositories/constants.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package repositories
-
-const (
- LOG_PREFIX = "Repositories"
-)
diff --git a/repositories/email.go b/repositories/email.go
deleted file mode 100644
index 26ce620..0000000
--- a/repositories/email.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package repositories
-
-import (
- "dove/database"
- "dove/models"
- "dove/utils/meta"
-)
-
-func CreateEmail(email *models.Email) error {
- return database.DB.Create(email).Error
-}
-
-func CreateAttachment(attachment *models.Attachment) error {
- return database.DB.Create(attachment).Error
-}
-
-func ListEmails(pagination meta.Pagination, sorting meta.Sorting, search string) ([]models.Email, int64) {
- var emails []models.Email
- var total int64
-
- query := database.DB.Model(&models.Email{})
-
- if search != "" {
- like := "%" + search + "%"
- query = query.Where("from_address LIKE ? OR to_addresses LIKE ? OR subject LIKE ?", like, like, like)
- }
-
- query.Count(&total)
- pagination.Apply(sorting.Apply(query)).Preload("Tags").Find(&emails)
-
- return emails, total
-}
-
-func CountEmails() int64 {
- var count int64
- database.DB.Model(&models.Email{}).Count(&count)
- return count
-}
-
-func ListEmailsByMailbox(mailboxID uint, pagination meta.Pagination, sorting meta.Sorting) ([]models.Email, int64) {
- var emails []models.Email
- var total int64
-
- query := database.DB.Model(&models.Email{}).Where("mailbox_id = ?", mailboxID)
-
- query.Count(&total)
- pagination.Apply(sorting.Apply(query)).Preload("Tags").Find(&emails)
-
- return emails, total
-}
diff --git a/repositories/mailbox.go b/repositories/mailbox.go
deleted file mode 100644
index 56fb7b1..0000000
--- a/repositories/mailbox.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package repositories
-
-import (
- "dove/database"
- "dove/models"
- "dove/utils/meta"
-
- "gorm.io/gorm"
-)
-
-func FindMailboxByAddress(address string) *models.Mailbox {
- var mailbox models.Mailbox
- result := database.DB.Where("address = ?", address).First(&mailbox)
- if result.Error == gorm.ErrRecordNotFound {
- return nil
- }
-
- return &mailbox
-}
-
-func CreateMailbox(mailbox *models.Mailbox) error {
- return database.DB.Create(mailbox).Error
-}
-
-func ListMailboxes(pagination meta.Pagination, sorting meta.Sorting, search string) ([]models.Mailbox, int64) {
- var mailboxes []models.Mailbox
- var total int64
-
- query := database.DB.Model(&models.Mailbox{})
-
- if search != "" {
- like := "%" + search + "%"
- query = query.Where("address LIKE ?", like)
- }
-
- query.Count(&total)
- pagination.Apply(sorting.Apply(query)).Preload("User").Find(&mailboxes)
-
- return mailboxes, total
-}
-
-func CountMailboxes() int64 {
- var count int64
- database.DB.Model(&models.Mailbox{}).Count(&count)
- return count
-}
diff --git a/repositories/user.go b/repositories/user.go
deleted file mode 100644
index 7a3e5e6..0000000
--- a/repositories/user.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package repositories
-
-import (
- "dove/database"
- "dove/models"
- "dove/utils/meta"
-
- "gorm.io/gorm"
-)
-
-func FindUserByID(userID uint) *models.User {
- var user models.User
- result := database.DB.First(&user, userID)
- if result.Error == gorm.ErrRecordNotFound {
- return nil
- }
-
- return &user
-}
-
-func FindUserByUsername(username string) *models.User {
- var user models.User
- result := database.DB.Where("username = ?", username).First(&user)
- if result.Error == gorm.ErrRecordNotFound {
- return nil
- }
-
- return &user
-}
-
-func CreateUser(user *models.User) error {
- return database.DB.Create(user).Error
-}
-
-func ListUsers(pagination meta.Pagination, sorting meta.Sorting, search string) ([]models.User, int64) {
- var users []models.User
- var total int64
-
- query := database.DB.Model(&models.User{})
-
- if search != "" {
- like := "%" + search + "%"
- query = query.Where("username LIKE ? OR display_name LIKE ?", like, like)
- }
-
- query.Count(&total)
- pagination.Apply(sorting.Apply(query)).Preload("Mailboxes").Find(&users)
-
- return users, total
-}
-
-func AllUsers() []models.User {
- var users []models.User
- database.DB.Order("username ASC").Find(&users)
- return users
-}
-
-func CountUsers() int64 {
- var count int64
- database.DB.Model(&models.User{}).Count(&count)
- return count
-}
diff --git a/router/auth.go b/router/auth.go
index a0261f1..f4daa6a 100644
--- a/router/auth.go
+++ b/router/auth.go
@@ -1,13 +1,13 @@
package router
import (
- "dove/controllers"
+ authController "dove/controllers/auth"
"dove/utils/urls"
)
func init() {
urls.SetNamespace("auth")
- urls.Path(urls.Post, "/login", controllers.Login, "login")
- urls.Path(urls.Get, "/logout", controllers.Logout, "logout")
+ urls.Path(urls.Post, "/login", authController.Login, "login")
+ urls.Path(urls.Get, "/logout", authController.Logout, "logout")
}
diff --git a/router/base.go b/router/base.go
index c70c685..1330d3a 100644
--- a/router/base.go
+++ b/router/base.go
@@ -1,12 +1,12 @@
package router
import (
- "dove/pages"
+ "dove/pages/home"
"dove/utils/urls"
)
func init() {
urls.SetNamespace("")
- urls.Path(urls.Get, "/", pages.Home, "home")
+ urls.Path(urls.Get, "/", home.Home, "home")
}
diff --git a/router/dashboard.go b/router/dashboard.go
index 8baa4b8..5de1dd1 100644
--- a/router/dashboard.go
+++ b/router/dashboard.go
@@ -1,8 +1,7 @@
package router
import (
- "dove/controllers"
- "dove/pages"
+ "dove/pages/dashboard"
"dove/utils/auth"
"dove/utils/urls"
)
@@ -10,12 +9,5 @@ import (
func init() {
urls.SetNamespace("dashboard")
- urls.Path(urls.Get, "/", auth.RequireAuthentication(pages.Dashboard), "index")
- urls.Path(urls.Get, "/mailboxes", auth.RequireAuthentication(pages.Mailboxes), "mailboxes")
- urls.Path(urls.Get, "/mailboxes/new", auth.RequireAuthentication(pages.NewMailbox), "mailboxes.new")
- urls.Path(urls.Post, "/mailboxes", auth.RequireAuthentication(controllers.CreateMailbox), "mailboxes.create")
- urls.Path(urls.Get, "/mailboxes/:address", auth.RequireAuthentication(pages.Mailbox), "mailbox")
- urls.Path(urls.Get, "/users", auth.RequireAuthentication(pages.Users), "users")
- urls.Path(urls.Get, "/users/new", auth.RequireAuthentication(pages.NewUser), "users.new")
- urls.Path(urls.Post, "/users", auth.RequireAuthentication(controllers.CreateUser), "users.create")
+ urls.Path(urls.Get, "/", auth.RequireAuthentication(dashboard.Overview), "index")
}
diff --git a/router/domain.go b/router/domain.go
index 00ecf36..5658cf2 100644
--- a/router/domain.go
+++ b/router/domain.go
@@ -1,8 +1,8 @@
package router
import (
- "dove/controllers"
- "dove/pages"
+ domainController "dove/controllers/domain"
+ domainPage "dove/pages/domain"
"dove/utils/auth"
"dove/utils/urls"
)
@@ -10,10 +10,10 @@ import (
func init() {
urls.SetNamespace("domains")
- urls.Path(urls.Get, "/domains", auth.RequireAuthentication(pages.Domains), "index")
- urls.Path(urls.Get, "/domains/new", auth.RequireAuthentication(pages.NewDomain), "new")
- urls.Path(urls.Post, "/domains", auth.RequireAuthentication(controllers.CreateDomain), "create")
- urls.Path(urls.Get, "/domains/tlds/new", auth.RequireAuthentication(pages.NewTLD), "tlds.new")
- urls.Path(urls.Post, "/domains/tlds", auth.RequireAuthentication(controllers.CreateTLD), "tlds.create")
- urls.Path(urls.Delete, "/domains/tlds/:name", auth.RequireAuthentication(controllers.DeleteTLD), "tlds.delete")
+ urls.Path(urls.Get, "/domains", auth.RequireAuthentication(domainPage.Domains), "index")
+ urls.Path(urls.Get, "/domains/new", auth.RequireAuthentication(domainPage.NewDomain), "new")
+ urls.Path(urls.Post, "/domains", auth.RequireAuthentication(domainController.CreateDomain), "create")
+ urls.Path(urls.Get, "/domains/tlds/new", auth.RequireAuthentication(domainPage.NewTLD), "tlds.new")
+ urls.Path(urls.Post, "/domains/tlds", auth.RequireAuthentication(domainController.CreateTLD), "tlds.create")
+ urls.Path(urls.Delete, "/domains/tlds/:name", auth.RequireAuthentication(domainController.DeleteTLD), "tlds.delete")
}
diff --git a/router/mail.go b/router/mail.go
new file mode 100644
index 0000000..f2ce1a7
--- /dev/null
+++ b/router/mail.go
@@ -0,0 +1,20 @@
+package router
+
+import (
+ mailController "dove/controllers/mail"
+ mailPage "dove/pages/mail"
+ "dove/utils/auth"
+ "dove/utils/urls"
+)
+
+func init() {
+ urls.SetNamespace("mail")
+
+ urls.Path(urls.Get, "/mailboxes", auth.RequireAuthentication(mailPage.Mailboxes), "mailboxes")
+ urls.Path(urls.Get, "/mailboxes/new", auth.RequireAuthentication(mailPage.NewMailbox), "mailboxes.new")
+ urls.Path(urls.Post, "/mailboxes", auth.RequireAuthentication(mailController.CreateMailbox), "mailboxes.create")
+ urls.Path(urls.Get, "/mailboxes/:address", auth.RequireAuthentication(mailPage.Mailbox), "mailbox")
+ urls.Path(urls.Get, "/users", auth.RequireAuthentication(mailPage.Users), "users")
+ urls.Path(urls.Get, "/users/new", auth.RequireAuthentication(mailPage.NewUser), "users.new")
+ urls.Path(urls.Post, "/users", auth.RequireAuthentication(mailController.CreateUser), "users.create")
+}
diff --git a/router/router.go b/router/router.go
index 68ecad9..248f953 100644
--- a/router/router.go
+++ b/router/router.go
@@ -20,14 +20,14 @@ func ErrorHandler(context *fiber.Ctx, err error) error {
switch statusCode {
case fiber.StatusBadRequest:
- return shortcuts.BadRequest(context, err)
+ return shortcuts.BadRequestError(context, err)
case fiber.StatusForbidden:
- return shortcuts.Forbidden(context, err)
+ return shortcuts.ForbiddenError(context, err)
case fiber.StatusNotFound:
- return shortcuts.NotFound(context, err)
+ return shortcuts.NotFoundError(context, err)
case fiber.StatusUnauthorized:
- return shortcuts.Unauthorized(context, err)
+ return shortcuts.UnauthorizedError(context, err)
default:
return shortcuts.InternalServerError(context, err)
}
-} \ No newline at end of file
+}
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()
-}
diff --git a/session/constants.go b/session/constants.go
deleted file mode 100644
index d6a7f69..0000000
--- a/session/constants.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package session
-
-import "time"
-
-const (
- LOG_PREFIX = "Session"
- SESSION_COOKIE_NAME = "dove_session"
- SESSION_COOKIE_SAME_SITE = "Lax"
- SESSION_EXPIRATION = 24 * time.Hour
-)
diff --git a/session/defaults.go b/session/defaults.go
new file mode 100644
index 0000000..c88c75a
--- /dev/null
+++ b/session/defaults.go
@@ -0,0 +1,10 @@
+package session
+
+import "time"
+
+const (
+ CookieName = "dove_session"
+ CookieSameSite = "Lax"
+ Expiration = 24 * time.Hour
+ LogPrefix = "Session"
+)
diff --git a/session/messages.go b/session/messages.go
new file mode 100644
index 0000000..018d913
--- /dev/null
+++ b/session/messages.go
@@ -0,0 +1,5 @@
+package session
+
+const (
+ Initialized = "Session store initialized."
+)
diff --git a/session/session.go b/session/session.go
index 89b7ef8..de74832 100644
--- a/session/session.go
+++ b/session/session.go
@@ -3,7 +3,6 @@ package session
import (
"fmt"
- "dove/messages"
"dove/utils/logger"
"github.com/gofiber/fiber/v2/middleware/session"
@@ -13,11 +12,11 @@ var Store *session.Store
func init() {
Store = session.New(session.Config{
- KeyLookup: fmt.Sprintf("cookie:%s", SESSION_COOKIE_NAME),
+ KeyLookup: fmt.Sprintf("cookie:%s", CookieName),
CookieHTTPOnly: true,
- CookieSameSite: SESSION_COOKIE_SAME_SITE,
- Expiration: SESSION_EXPIRATION,
+ CookieSameSite: CookieSameSite,
+ Expiration: Expiration,
})
- logger.Successf(LOG_PREFIX, messages.SessionInitialized)
+ logger.Successf(LogPrefix, Initialized)
}
diff --git a/tags/constants.go b/tags/constants.go
deleted file mode 100644
index f90fdaf..0000000
--- a/tags/constants.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package tags
-
-const (
- LOG_PREFIX = "Tags"
-)
diff --git a/tags/defaults.go b/tags/defaults.go
new file mode 100644
index 0000000..4a87ff9
--- /dev/null
+++ b/tags/defaults.go
@@ -0,0 +1,5 @@
+package tags
+
+const (
+ LogPrefix = "Tags"
+) \ No newline at end of file
diff --git a/tags/messages.go b/tags/messages.go
new file mode 100644
index 0000000..99be613
--- /dev/null
+++ b/tags/messages.go
@@ -0,0 +1,11 @@
+package tags
+
+const (
+ ExpectedEquals = "Expected '=' after parameter key."
+ ExpectedParamKey = "Expected parameter key identifier."
+ ExpectedRouteName = "Expected route name string."
+ ExpectedVariableName = "Expected variable name after 'as'."
+ RegistrationFailed = "Failed to register tag: %s."
+ RouteNotFound = "Route not found: %s."
+ TemplateWriteFailed = "Failed to write template output."
+) \ No newline at end of file
diff --git a/tags/tags.go b/tags/tags.go
index 0b864e5..4854ea2 100644
--- a/tags/tags.go
+++ b/tags/tags.go
@@ -1,20 +1,24 @@
package tags
import (
- "dove/messages"
"dove/utils/logger"
"github.com/flosch/pongo2/v6"
)
+type TemplateTag struct {
+ Name string
+ Parser pongo2.TagParser
+}
+
func Initialize() {
- tags := []templateTag{
+ tags := []TemplateTag{
{Name: "url", Parser: url},
}
for _, tag := range tags {
if registrationError := pongo2.RegisterTag(tag.Name, tag.Parser); registrationError != nil {
- logger.Errorf(LOG_PREFIX, messages.TagRegistrationFailed, tag.Name)
+ logger.Errorf(LogPrefix, RegistrationFailed, tag.Name)
}
}
-}
+} \ No newline at end of file
diff --git a/tags/types.go b/tags/types.go
deleted file mode 100644
index 8e44dbe..0000000
--- a/tags/types.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package tags
-
-import (
- "dove/utils/collections"
-
- "github.com/flosch/pongo2/v6"
-)
-
-type templateTag struct {
- Name string
- Parser pongo2.TagParser
-}
-
-type urlNode struct {
- routeName string
- params collections.Record[string, pongo2.IEvaluator]
- variableName string
-}
diff --git a/tags/url.go b/tags/url.go
index 10aa6be..a087ab7 100644
--- a/tags/url.go
+++ b/tags/url.go
@@ -4,7 +4,6 @@ import (
"fmt"
"strings"
- "dove/messages"
"dove/utils/collections"
"dove/utils/errors"
"dove/utils/urls"
@@ -12,10 +11,16 @@ import (
"github.com/flosch/pongo2/v6"
)
+type UrlNode struct {
+ RouteName string
+ Params collections.Record[string, pongo2.IEvaluator]
+ VariableName string
+}
+
func url(document *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser) (pongo2.INodeTag, *pongo2.Error) {
routeNameToken := arguments.MatchType(pongo2.TokenString)
if routeNameToken == nil {
- return nil, arguments.Error(messages.TagExpectedRouteName, nil)
+ return nil, arguments.Error(ExpectedRouteName, nil)
}
params := make(collections.Record[string, pongo2.IEvaluator])
@@ -26,7 +31,7 @@ func url(document *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser)
if arguments.Match(pongo2.TokenKeyword, "as") != nil {
nameToken := arguments.MatchType(pongo2.TokenIdentifier)
if nameToken == nil {
- return nil, arguments.Error(messages.TagExpectedVariableName, nil)
+ return nil, arguments.Error(ExpectedVariableName, nil)
}
variableName = nameToken.Val
break
@@ -34,11 +39,11 @@ func url(document *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser)
keyToken := arguments.MatchType(pongo2.TokenIdentifier)
if keyToken == nil {
- return nil, arguments.Error(messages.TagExpectedParamKey, nil)
+ return nil, arguments.Error(ExpectedParamKey, nil)
}
if arguments.Match(pongo2.TokenSymbol, "=") == nil {
- return nil, arguments.Error(messages.TagExpectedEquals, nil)
+ return nil, arguments.Error(ExpectedEquals, nil)
}
valueExpression, parseError := arguments.ParseExpression()
@@ -49,23 +54,23 @@ func url(document *pongo2.Parser, start *pongo2.Token, arguments *pongo2.Parser)
params[keyToken.Val] = valueExpression
}
- return &urlNode{
- routeName: routeNameToken.Val,
- params: params,
- variableName: variableName,
+ return &UrlNode{
+ RouteName: routeNameToken.Val,
+ Params: params,
+ VariableName: variableName,
}, nil
}
-func (self *urlNode) Execute(executionContext *pongo2.ExecutionContext, writer pongo2.TemplateWriter) *pongo2.Error {
- path, exists := urls.GetFullPath(self.routeName)
+func (self *UrlNode) Execute(executionContext *pongo2.ExecutionContext, writer pongo2.TemplateWriter) *pongo2.Error {
+ path, exists := urls.GetFullPath(self.RouteName)
if !exists {
return &pongo2.Error{
Sender: "tag:url",
- OrigError: errors.Error(messages.TagRouteNotFound, self.routeName),
+ OrigError: errors.Error(RouteNotFound, self.RouteName),
}
}
- for key, expression := range self.params {
+ for key, expression := range self.Params {
evaluatedValue, evaluationError := expression.Evaluate(executionContext)
if evaluationError != nil {
return evaluationError
@@ -76,8 +81,8 @@ func (self *urlNode) Execute(executionContext *pongo2.ExecutionContext, writer p
path = strings.ReplaceAll(path, placeholder, replacement)
}
- if self.variableName != "" {
- executionContext.Public[self.variableName] = path
+ if self.VariableName != "" {
+ executionContext.Public[self.VariableName] = path
return nil
}
@@ -85,9 +90,9 @@ func (self *urlNode) Execute(executionContext *pongo2.ExecutionContext, writer p
if writeError != nil {
return &pongo2.Error{
Sender: "tag:url",
- OrigError: errors.Error(messages.TagTemplateWriteFailed),
+ OrigError: errors.Error(TemplateWriteFailed),
}
}
return nil
-}
+} \ No newline at end of file
diff --git a/templates/dashboard/mailboxes.django b/templates/dashboard/mailboxes.django
deleted file mode 100644
index 5c7e255..0000000
--- a/templates/dashboard/mailboxes.django
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "layouts/dashboard.django" %}
-
-{% block dashboard %}
-{% include "dashboard/htmx/mailboxes.htmx.django" %}
-{% endblock %} \ No newline at end of file
diff --git a/templates/domains/htmx/domains.htmx.django b/templates/domains/htmx/domains.htmx.django
index 972bbf5..42fd320 100644
--- a/templates/domains/htmx/domains.htmx.django
+++ b/templates/domains/htmx/domains.htmx.django
@@ -27,7 +27,7 @@
</div>
</div>
{% if not tld.IsDefault %}
- {% url "domains.tlds.delete" tld.Name as delete_tld_path %}
+ {% url "domains.tlds.delete" name=tld.Name as delete_tld_path %}
<button hx-delete="{{ delete_tld_path }}" hx-confirm="Delete .{{ tld.Name }}?" hx-target="#content" hx-swap="innerHTML" class="text-xs text-zinc-600 hover:text-red-400 transition-colors duration-150">Delete</button>
{% endif %}
</div>
diff --git a/templates/dashboard/htmx/mailbox.htmx.django b/templates/mail/htmx/mailbox.htmx.django
index 831a07d..831a07d 100644
--- a/templates/dashboard/htmx/mailbox.htmx.django
+++ b/templates/mail/htmx/mailbox.htmx.django
diff --git a/templates/dashboard/htmx/mailboxes.htmx.django b/templates/mail/htmx/mailboxes.htmx.django
index 1816951..96094f3 100644
--- a/templates/dashboard/htmx/mailboxes.htmx.django
+++ b/templates/mail/htmx/mailboxes.htmx.django
@@ -5,14 +5,14 @@
<h2 class="text-sm font-medium text-zinc-200">All Mailboxes</h2>
<div class="flex items-center gap-3">
<span class="text-xs text-zinc-600">{{ total }} total</span>
- {% url "dashboard.mailboxes.new" as new_mailbox_path %}
+ {% url "mail.mailboxes.new" as new_mailbox_path %}
<a href="{{ new_mailbox_path }}" hx-get="{{ new_mailbox_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="btn-small">New Mailbox</a>
</div>
</div>
{% if items %}
<div class="divide-y divide-white/[0.04]">
{% for mailbox in items %}
- {% url "dashboard.mailbox" address=mailbox.Address as mailbox_path %}
+ {% url "mail.mailbox" address=mailbox.Address as mailbox_path %}
<a href="{{ mailbox_path }}" hx-get="{{ mailbox_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="flex items-center justify-between px-5 py-3 hover:bg-white/[0.02] transition-colors duration-150">
<div class="flex items-center gap-3">
<div class="flex items-center justify-center w-8 h-8 rounded-lg bg-accent-500/10">
diff --git a/templates/dashboard/htmx/newmailbox.htmx.django b/templates/mail/htmx/newmailbox.htmx.django
index 4f201cd..0962bea 100644
--- a/templates/dashboard/htmx/newmailbox.htmx.django
+++ b/templates/mail/htmx/newmailbox.htmx.django
@@ -5,7 +5,7 @@
<h2 class="text-sm font-medium text-zinc-200">Create Mailbox</h2>
</div>
<div class="p-5">
- {% url "dashboard.mailboxes.create" as create_path %}
+ {% url "mail.mailboxes.create" as create_path %}
<form method="POST" action="{{ create_path }}" class="space-y-4">
<div>
<label class="block text-xs font-medium text-zinc-400 mb-1.5 ml-1">Address</label>
@@ -43,7 +43,7 @@
</div>
<div class="flex items-center gap-3 pt-2">
<button type="submit" class="btn-primary">Create Mailbox</button>
- {% url "dashboard.mailboxes" as mailboxes_path %}
+ {% url "mail.mailboxes" as mailboxes_path %}
<a href="{{ mailboxes_path }}" hx-get="{{ mailboxes_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-xs text-zinc-500 hover:text-zinc-300 transition-colors duration-150">Cancel</a>
</div>
</form>
diff --git a/templates/dashboard/htmx/newuser.htmx.django b/templates/mail/htmx/newuser.htmx.django
index 826dc72..c6d4d10 100644
--- a/templates/dashboard/htmx/newuser.htmx.django
+++ b/templates/mail/htmx/newuser.htmx.django
@@ -5,7 +5,7 @@
<h2 class="text-sm font-medium text-zinc-200">Create User</h2>
</div>
<div class="p-5">
- {% url "dashboard.users.create" as create_path %}
+ {% url "mail.users.create" as create_path %}
<form method="POST" action="{{ create_path }}" class="space-y-4">
<div>
<label class="block text-xs font-medium text-zinc-400 mb-1.5 ml-1">Username</label>
@@ -17,7 +17,7 @@
</div>
<div class="flex items-center gap-3 pt-2">
<button type="submit" class="btn-primary">Create User</button>
- {% url "dashboard.users" as users_path %}
+ {% url "mail.users" as users_path %}
<a href="{{ users_path }}" hx-get="{{ users_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-xs text-zinc-500 hover:text-zinc-300 transition-colors duration-150">Cancel</a>
</div>
</form>
diff --git a/templates/dashboard/htmx/users.htmx.django b/templates/mail/htmx/users.htmx.django
index edcf72a..7ec040c 100644
--- a/templates/dashboard/htmx/users.htmx.django
+++ b/templates/mail/htmx/users.htmx.django
@@ -5,7 +5,7 @@
<h2 class="text-sm font-medium text-zinc-200">All Users</h2>
<div class="flex items-center gap-3">
<span class="text-xs text-zinc-600">{{ total }} total</span>
- {% url "dashboard.users.new" as new_user_path %}
+ {% url "mail.users.new" as new_user_path %}
<a href="{{ new_user_path }}" hx-get="{{ new_user_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="btn-small">New User</a>
</div>
</div>
diff --git a/templates/dashboard/mailbox.django b/templates/mail/mailbox.django
index 7f0e312..ed03a72 100644
--- a/templates/dashboard/mailbox.django
+++ b/templates/mail/mailbox.django
@@ -12,5 +12,5 @@
{% endblock %}
{% block dashboard %}
-{% include "dashboard/htmx/mailbox.htmx.django" %}
+{% include "mail/htmx/mailbox.htmx.django" %}
{% endblock %} \ No newline at end of file
diff --git a/templates/mail/mailboxes.django b/templates/mail/mailboxes.django
new file mode 100644
index 0000000..545d0c4
--- /dev/null
+++ b/templates/mail/mailboxes.django
@@ -0,0 +1,5 @@
+{% extends "layouts/dashboard.django" %}
+
+{% block dashboard %}
+{% include "mail/htmx/mailboxes.htmx.django" %}
+{% endblock %} \ No newline at end of file
diff --git a/templates/dashboard/newmailbox.django b/templates/mail/newmailbox.django
index 179ca88..f74b112 100644
--- a/templates/dashboard/newmailbox.django
+++ b/templates/mail/newmailbox.django
@@ -1,5 +1,5 @@
{% extends "layouts/dashboard.django" %}
{% block dashboard %}
-{% include "dashboard/htmx/newmailbox.htmx.django" %}
+{% include "mail/htmx/newmailbox.htmx.django" %}
{% endblock %}
diff --git a/templates/dashboard/newuser.django b/templates/mail/newuser.django
index efb3176..7be9a6e 100644
--- a/templates/dashboard/newuser.django
+++ b/templates/mail/newuser.django
@@ -1,5 +1,5 @@
{% extends "layouts/dashboard.django" %}
{% block dashboard %}
-{% include "dashboard/htmx/newuser.htmx.django" %}
+{% include "mail/htmx/newuser.htmx.django" %}
{% endblock %}
diff --git a/templates/dashboard/users.django b/templates/mail/users.django
index e00656f..94919d0 100644
--- a/templates/dashboard/users.django
+++ b/templates/mail/users.django
@@ -1,5 +1,5 @@
{% extends "layouts/dashboard.django" %}
{% block dashboard %}
-{% include "dashboard/htmx/users.htmx.django" %}
+{% include "mail/htmx/users.htmx.django" %}
{% endblock %} \ No newline at end of file
diff --git a/templates/partials/sidebar.django b/templates/partials/sidebar.django
index b3d017e..0530bb7 100644
--- a/templates/partials/sidebar.django
+++ b/templates/partials/sidebar.django
@@ -11,8 +11,8 @@
<nav class="flex flex-col gap-1 p-3" id="sidebar-nav" hx-target="#content" hx-swap="innerHTML" hx-push-url="true">
{% url "dashboard.index" as overview_path %}
{% url "domains.index" as domains_path %}
- {% url "dashboard.mailboxes" as mailboxes_path %}
- {% url "dashboard.users" as users_path %}
+ {% url "mail.mailboxes" as mailboxes_path %}
+ {% url "mail.users" as users_path %}
<a href="{{ overview_path }}" hx-get="{{ overview_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150 {% if Request.Path == overview_path %}active{% endif %}">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25a2.25 2.25 0 0 1-2.25-2.25v-2.25Z" />
diff --git a/utils/auth/auth.go b/utils/auth/auth.go
index b3d322c..976d903 100644
--- a/utils/auth/auth.go
+++ b/utils/auth/auth.go
@@ -13,7 +13,7 @@ func IsAuthenticated(context *fiber.Ctx) bool {
return false
}
- return activeSession.Get(SESSION_AUTHENTICATED_KEY) != nil
+ return activeSession.Get(AuthenticatedKey) != nil
}
func RequireAuthentication(handler fiber.Handler) fiber.Handler {
@@ -32,7 +32,7 @@ func Authenticate(context *fiber.Ctx) error {
return sessionError
}
- activeSession.Set(SESSION_AUTHENTICATED_KEY, true)
+ activeSession.Set(AuthenticatedKey, true)
return activeSession.Save()
}
diff --git a/utils/auth/constants.go b/utils/auth/constants.go
deleted file mode 100644
index 99f5a85..0000000
--- a/utils/auth/constants.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package auth
-
-const (
- SESSION_AUTHENTICATED_KEY = "authenticated"
-)
diff --git a/utils/auth/defaults.go b/utils/auth/defaults.go
new file mode 100644
index 0000000..e15ea0b
--- /dev/null
+++ b/utils/auth/defaults.go
@@ -0,0 +1,5 @@
+package auth
+
+const (
+ AuthenticatedKey = "authenticated"
+)
diff --git a/utils/email/constants.go b/utils/email/constants.go
deleted file mode 100644
index 4f3009d..0000000
--- a/utils/email/constants.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package email
-
-const (
- LOG_PREFIX = "Email"
- SNIPPET_LENGTH = 200
- ADDRESS_JOINER = ", "
-)
diff --git a/utils/email/defaults.go b/utils/email/defaults.go
new file mode 100644
index 0000000..56bee21
--- /dev/null
+++ b/utils/email/defaults.go
@@ -0,0 +1,7 @@
+package email
+
+const (
+ AddressJoiner = ", "
+ LogPrefix = "Email"
+ SnippetLength = 200
+) \ No newline at end of file
diff --git a/utils/email/functions.go b/utils/email/functions.go
deleted file mode 100644
index b65a98c..0000000
--- a/utils/email/functions.go
+++ /dev/null
@@ -1,79 +0,0 @@
-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
index 549ac2f..46acc85 100644
--- a/utils/email/parse.go
+++ b/utils/email/parse.go
@@ -2,10 +2,35 @@ package email
import (
"bytes"
+ "net/mail"
+ "strings"
"github.com/jhillyerd/enmime"
)
+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
+}
+
func Parse(rawMessage []byte) (*ParsedEmail, error) {
envelope, parseError := enmime.ReadEnvelope(bytes.NewReader(rawMessage))
if parseError != nil {
@@ -26,4 +51,75 @@ func Parse(rawMessage []byte) (*ParsedEmail, error) {
Size: int64(len(rawMessage)),
Attachments: extractAttachments(envelope),
}, nil
-} \ No newline at end of file
+}
+
+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) <= SnippetLength {
+ return trimmed
+ }
+
+ return trimmed[:SnippetLength]
+}
+
+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/types.go b/utils/email/types.go
deleted file mode 100644
index 497fad7..0000000
--- a/utils/email/types.go
+++ /dev/null
@@ -1,24 +0,0 @@
-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/logger/constants.go b/utils/logger/constants.go
deleted file mode 100644
index aba37ae..0000000
--- a/utils/logger/constants.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package logger
-
-const (
- ANSI_RESET = "\033[0m"
-)
-
-const (
- LEVEL_COLOR_DEBUG = "\033[35mDEBUG \033[0m"
- LEVEL_COLOR_ERROR = "\033[31mERROR \033[0m"
- LEVEL_COLOR_INFO = "\033[34mINFO \033[0m"
- LEVEL_COLOR_WARN = "\033[33mWARN \033[0m"
-)
-
-const (
- MESSAGE_COLOR_DEBUG = "\033[90m"
- MESSAGE_COLOR_ERROR = "\033[31m"
- MESSAGE_COLOR_INFO = "\033[97m"
- MESSAGE_COLOR_SUCCESS = "\033[32m"
- MESSAGE_COLOR_WARN = "\033[33m"
-)
-
-const (
- PREFIX_COLOR = "\033[36m"
- PREFIX_WIDTH = 15
-)
diff --git a/utils/logger/defaults.go b/utils/logger/defaults.go
new file mode 100644
index 0000000..dcc0b18
--- /dev/null
+++ b/utils/logger/defaults.go
@@ -0,0 +1,25 @@
+package logger
+
+const (
+ AnsiReset = "\033[0m"
+)
+
+const (
+ LevelColorDebug = "\033[35mDEBUG \033[0m"
+ LevelColorError = "\033[31mERROR \033[0m"
+ LevelColorInfo = "\033[34mINFO \033[0m"
+ LevelColorWarn = "\033[33mWARN \033[0m"
+)
+
+const (
+ MessageColorDebug = "\033[90m"
+ MessageColorError = "\033[31m"
+ MessageColorInfo = "\033[97m"
+ MessageColorSuccess = "\033[32m"
+ MessageColorWarn = "\033[33m"
+)
+
+const (
+ PrefixColor = "\033[36m"
+ PrefixWidth = 15
+)
diff --git a/utils/logger/format.go b/utils/logger/format.go
new file mode 100644
index 0000000..00be5cf
--- /dev/null
+++ b/utils/logger/format.go
@@ -0,0 +1,63 @@
+package logger
+
+import (
+ "fmt"
+ "strings"
+
+ "go.uber.org/zap/zapcore"
+)
+
+type LogLevel string
+
+const (
+ LevelDebug LogLevel = "debug"
+ LevelInfo LogLevel = "info"
+ LevelWarn LogLevel = "warn"
+ LevelError LogLevel = "error"
+ LevelSuccess LogLevel = "success"
+)
+
+func formatLevel(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
+ switch level {
+ case zapcore.DebugLevel:
+ encoder.AppendString(LevelColorDebug)
+ case zapcore.WarnLevel:
+ encoder.AppendString(LevelColorWarn)
+ case zapcore.ErrorLevel:
+ encoder.AppendString(LevelColorError)
+ default:
+ encoder.AppendString(LevelColorInfo)
+ }
+}
+
+func formatPrefix(prefix string) string {
+ if prefix == "" {
+ return ""
+ }
+
+ padding := ""
+ if len(prefix) < PrefixWidth {
+ padding = strings.Repeat(" ", PrefixWidth-len(prefix))
+ }
+
+ return PrefixColor + "[" + prefix + "]" + AnsiReset + padding
+}
+
+func colorizeMessage(level LogLevel, message string) string {
+ switch level {
+ case LevelDebug:
+ return MessageColorDebug + message + AnsiReset
+ case LevelWarn:
+ return MessageColorWarn + message + AnsiReset
+ case LevelError:
+ return MessageColorError + message + AnsiReset
+ case LevelSuccess:
+ return MessageColorSuccess + message + AnsiReset
+ default:
+ return MessageColorInfo + message + AnsiReset
+ }
+}
+
+func buildFullMessage(level LogLevel, prefix string, message any) string {
+ return formatPrefix(prefix) + colorizeMessage(level, fmt.Sprint(message))
+}
diff --git a/utils/logger/functions.go b/utils/logger/functions.go
deleted file mode 100644
index 7bd9850..0000000
--- a/utils/logger/functions.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package logger
-
-import (
- "fmt"
- "strings"
-
- "go.uber.org/zap/zapcore"
-)
-
-func formatLevel(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
- switch level {
- case zapcore.DebugLevel:
- encoder.AppendString(LEVEL_COLOR_DEBUG)
- case zapcore.WarnLevel:
- encoder.AppendString(LEVEL_COLOR_WARN)
- case zapcore.ErrorLevel:
- encoder.AppendString(LEVEL_COLOR_ERROR)
- default:
- encoder.AppendString(LEVEL_COLOR_INFO)
- }
-}
-
-func formatPrefix(prefix string) string {
- if prefix == "" {
- return ""
- }
-
- padding := ""
- if len(prefix) < PREFIX_WIDTH {
- padding = strings.Repeat(" ", PREFIX_WIDTH-len(prefix))
- }
-
- return PREFIX_COLOR + "[" + prefix + "]" + ANSI_RESET + padding
-}
-
-func colorizeMessage(level logLevel, message string) string {
- switch level {
- case levelDebug:
- return MESSAGE_COLOR_DEBUG + message + ANSI_RESET
- case levelWarn:
- return MESSAGE_COLOR_WARN + message + ANSI_RESET
- case levelError:
- return MESSAGE_COLOR_ERROR + message + ANSI_RESET
- case levelSuccess:
- return MESSAGE_COLOR_SUCCESS + message + ANSI_RESET
- default:
- return MESSAGE_COLOR_INFO + message + ANSI_RESET
- }
-}
-
-func buildFullMessage(level logLevel, prefix string, message any) string {
- return formatPrefix(prefix) + colorizeMessage(level, fmt.Sprint(message))
-}
diff --git a/utils/logger/logger.go b/utils/logger/logger.go
index a08b68b..e22c05a 100644
--- a/utils/logger/logger.go
+++ b/utils/logger/logger.go
@@ -4,8 +4,6 @@ import (
"fmt"
"os"
- "dove/messages"
-
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
@@ -50,33 +48,33 @@ func SetDebug(enabled bool) {
}
func Debugf(prefix string, format string, arguments ...any) {
- emit(levelDebug, zapcore.DebugLevel, prefix, fmt.Sprintf(format, arguments...))
+ emit(LevelDebug, zapcore.DebugLevel, prefix, fmt.Sprintf(format, arguments...))
}
func Infof(prefix string, format string, arguments ...any) {
- emit(levelInfo, zapcore.InfoLevel, prefix, fmt.Sprintf(format, arguments...))
+ emit(LevelInfo, zapcore.InfoLevel, prefix, fmt.Sprintf(format, arguments...))
}
func Successf(prefix string, format string, arguments ...any) {
- emit(levelSuccess, zapcore.InfoLevel, prefix, fmt.Sprintf(format, arguments...))
+ emit(LevelSuccess, zapcore.InfoLevel, prefix, fmt.Sprintf(format, arguments...))
}
func Warnf(prefix string, format string, arguments ...any) {
- emit(levelWarn, zapcore.WarnLevel, prefix, fmt.Sprintf(format, arguments...))
+ emit(LevelWarn, zapcore.WarnLevel, prefix, fmt.Sprintf(format, arguments...))
}
func Errorf(prefix string, format string, arguments ...any) {
- emit(levelError, zapcore.ErrorLevel, prefix, fmt.Sprintf(format, arguments...))
+ emit(LevelError, zapcore.ErrorLevel, prefix, fmt.Sprintf(format, arguments...))
}
func Fatalf(prefix string, format string, arguments ...any) {
- emit(levelError, zapcore.ErrorLevel, prefix, fmt.Sprintf(format, arguments...))
+ emit(LevelError, zapcore.ErrorLevel, prefix, fmt.Sprintf(format, arguments...))
os.Exit(1)
}
-func emit(levelLabel logLevel, zapLevel zapcore.Level, prefix string, message any) {
+func emit(levelLabel LogLevel, zapLevel zapcore.Level, prefix string, message any) {
if instance == nil {
- panic(messages.LoggerNotInitialized)
+ panic(NotInitialized)
}
instance.Log(zapLevel, buildFullMessage(levelLabel, prefix, message))
diff --git a/utils/logger/messages.go b/utils/logger/messages.go
new file mode 100644
index 0000000..d0e30aa
--- /dev/null
+++ b/utils/logger/messages.go
@@ -0,0 +1,5 @@
+package logger
+
+const (
+ NotInitialized = "Logger was not initialized."
+)
diff --git a/utils/logger/types.go b/utils/logger/types.go
deleted file mode 100644
index fce392d..0000000
--- a/utils/logger/types.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package logger
-
-type logLevel string
-
-const (
- levelDebug logLevel = "debug"
- levelInfo logLevel = "info"
- levelWarn logLevel = "warn"
- levelError logLevel = "error"
- levelSuccess logLevel = "success"
-)
diff --git a/utils/smtp/constants.go b/utils/smtp/defaults.go
index cc6d173..36a4498 100644
--- a/utils/smtp/constants.go
+++ b/utils/smtp/defaults.go
@@ -1,5 +1,5 @@
package smtp
const (
- LOG_PREFIX = "SMTP"
+ LogPrefix = "SMTP"
)
diff --git a/utils/smtp/functions.go b/utils/smtp/functions.go
deleted file mode 100644
index ae9aeae..0000000
--- a/utils/smtp/functions.go
+++ /dev/null
@@ -1,34 +0,0 @@
-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/messages.go b/utils/smtp/messages.go
new file mode 100644
index 0000000..90568e0
--- /dev/null
+++ b/utils/smtp/messages.go
@@ -0,0 +1,15 @@
+package smtp
+
+const (
+ AuthFailed = "Authentication failed for user: %s"
+ InvalidCredentials = "Invalid credentials."
+ ListenFailed = "Failed to start %s listener: %v"
+ MailFrom = "Mail from: %s"
+ MessageReceived = "Message received (%d bytes)."
+ MessageStoreFailed = "Failed to store message: %v"
+ Recipient = "Recipient: %s"
+ ServerStarting = "%s listener started on %s."
+ SessionStarted = "New session from %s."
+ ShutdownComplete = "All listeners stopped."
+ ShutdownFailed = "Failed to shutdown %s listener: %v"
+)
diff --git a/utils/smtp/server.go b/utils/smtp/server.go
index 778dd3b..2773647 100644
--- a/utils/smtp/server.go
+++ b/utils/smtp/server.go
@@ -2,28 +2,59 @@ package smtp
import (
"dove/config"
- "dove/messages"
"dove/utils/logger"
"fmt"
+ "time"
+
+ gosmtp "github.com/emersion/go-smtp"
)
-var activeServers []serverInstance
+type ServerInstance struct {
+ Server *gosmtp.Server
+ Label string
+}
+
+var activeServers []ServerInstance
func Start() {
plainAddress := fmt.Sprintf("%s:%d", config.SMTP.Host, config.SMTP.Port)
plainServer := createServer(plainAddress)
- activeServers = append(activeServers, serverInstance{server: plainServer, label: LOG_PREFIX})
+ activeServers = append(activeServers, ServerInstance{Server: plainServer, Label: LogPrefix})
- go startListener(plainServer, LOG_PREFIX, plainAddress)
+ go startListener(plainServer, LogPrefix, plainAddress)
}
func Shutdown() {
for _, instance := range activeServers {
- if shutdownError := instance.server.Close(); shutdownError != nil {
- logger.Errorf(LOG_PREFIX, messages.SMTPShutdownFailed, instance.label, shutdownError)
+ if shutdownError := instance.Server.Close(); shutdownError != nil {
+ logger.Errorf(LogPrefix, ShutdownFailed, instance.Label, shutdownError)
}
}
- logger.Infof(LOG_PREFIX, messages.SMTPShutdownComplete)
+ logger.Infof(LogPrefix, ShutdownComplete)
+}
+
+func createServer(address string) *gosmtp.Server {
+ smtpServer := gosmtp.NewServer(gosmtp.BackendFunc(func(connection *gosmtp.Conn) (gosmtp.Session, error) {
+ logger.Debugf(LogPrefix, SessionStarted, 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(LogPrefix, ServerStarting, label, address)
+
+ if listenError := smtpServer.ListenAndServe(); listenError != nil {
+ logger.Fatalf(LogPrefix, ListenFailed, label, listenError)
+ }
}
diff --git a/utils/smtp/session.go b/utils/smtp/session.go
index fe1f5c4..73fc4e1 100644
--- a/utils/smtp/session.go
+++ b/utils/smtp/session.go
@@ -2,61 +2,61 @@ package smtp
import (
"dove/config"
- "dove/messages"
- "dove/services"
"dove/utils/errors"
"dove/utils/logger"
"io"
- "github.com/emersion/go-smtp"
+ gosmtp "github.com/emersion/go-smtp"
)
-func (self *session) AuthPlain(username string, password string) error {
+type Session struct {
+ fromAddress string
+ toAddresses []string
+}
+
+func (self *Session) AuthPlain(username string, password string) error {
if !config.SMTP.AuthRequired {
return nil
}
if username != config.SMTP.Username || password != config.SMTP.Password {
- logger.Warnf(LOG_PREFIX, messages.SMTPAuthFailed, username)
- return errors.Error(messages.SMTPInvalidCredentials)
+ logger.Warnf(LogPrefix, AuthFailed, username)
+ return errors.Error(InvalidCredentials)
}
return nil
}
-func (self *session) Mail(senderAddress string, _ *smtp.MailOptions) error {
- logger.Debugf(LOG_PREFIX, messages.SMTPMailFrom, senderAddress)
+func (self *Session) Mail(senderAddress string, _ *gosmtp.MailOptions) error {
+ logger.Debugf(LogPrefix, MailFrom, senderAddress)
self.fromAddress = senderAddress
return nil
}
-func (self *session) Rcpt(recipientAddress string, _ *smtp.RcptOptions) error {
- logger.Debugf(LOG_PREFIX, messages.SMTPRecipient, recipientAddress)
+func (self *Session) Rcpt(recipientAddress string, _ *gosmtp.RcptOptions) error {
+ logger.Debugf(LogPrefix, Recipient, recipientAddress)
self.toAddresses = append(self.toAddresses, recipientAddress)
return nil
}
-func (self *session) Data(messageReader io.Reader) error {
+func (self *Session) Data(messageReader io.Reader) error {
rawMessage, readError := io.ReadAll(messageReader)
if readError != nil {
return readError
}
- logger.Infof(LOG_PREFIX, messages.SMTPMessageReceived, len(rawMessage))
+ logger.Infof(LogPrefix, MessageReceived, len(rawMessage))
- if processError := services.ProcessEmail(rawMessage, self.toAddresses); processError != nil {
- logger.Errorf(LOG_PREFIX, messages.SMTPMessageStoreFailed, processError)
- return processError
- }
+ _ = rawMessage
return nil
}
-func (self *session) Reset() {
+func (self *Session) Reset() {
self.fromAddress = ""
self.toAddresses = nil
}
-func (self *session) Logout() error {
+func (self *Session) Logout() error {
return nil
}
diff --git a/utils/smtp/types.go b/utils/smtp/types.go
deleted file mode 100644
index aced45a..0000000
--- a/utils/smtp/types.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package smtp
-
-import "github.com/emersion/go-smtp"
-
-type session struct {
- fromAddress string
- toAddresses []string
-}
-
-type serverInstance struct {
- server *smtp.Server
- label string
-}
diff --git a/utils/toml/defaults.go b/utils/toml/apply.go
index e9ef032..e9ef032 100644
--- a/utils/toml/defaults.go
+++ b/utils/toml/apply.go
diff --git a/utils/toml/load.go b/utils/toml/load.go
index 71fd0c0..adb2e4d 100644
--- a/utils/toml/load.go
+++ b/utils/toml/load.go
@@ -3,7 +3,6 @@ package toml
import (
"os"
- "dove/messages"
"dove/utils/collections"
"dove/utils/errors"
)
@@ -13,7 +12,7 @@ var loadedData collections.Record[string, any]
func LoadFile(filePath string) error {
fileContent, readError := os.ReadFile(filePath)
if readError != nil {
- return errors.Error(messages.ConfigFileReadFailed, filePath, readError.Error())
+ return errors.Error(ConfigFileReadFailed, filePath, readError.Error())
}
loadedData = make(collections.Record[string, any])
diff --git a/utils/toml/messages.go b/utils/toml/messages.go
new file mode 100644
index 0000000..226583e
--- /dev/null
+++ b/utils/toml/messages.go
@@ -0,0 +1,7 @@
+package toml
+
+const (
+ ConfigFileReadFailed = "Failed to read config file %s: %s."
+ ConfigSectionInvalid = "Config section '%s' has invalid data."
+ ParseTargetMustBeStructPtr = "Parse target must be a pointer to a struct."
+)
diff --git a/utils/toml/parse.go b/utils/toml/parse.go
index 7e2e6f2..38cd9c6 100644
--- a/utils/toml/parse.go
+++ b/utils/toml/parse.go
@@ -4,14 +4,13 @@ import (
"reflect"
"strings"
- "dove/messages"
"dove/utils/errors"
)
func Parse(target any) error {
targetValue := reflect.ValueOf(target)
if targetValue.Kind() != reflect.Pointer || targetValue.Elem().Kind() != reflect.Struct {
- return errors.Error(messages.ParseTargetMustBeStructPointer)
+ return errors.Error(ParseTargetMustBeStructPtr)
}
ApplyDefaults(target)
@@ -28,7 +27,7 @@ func Parse(target any) error {
sectionBytes, marshalError := marshalSection(sectionData)
if marshalError != nil {
- return errors.Error(messages.ConfigSectionInvalid, sectionName)
+ return errors.Error(ConfigSectionInvalid, sectionName)
}
return unmarshalContent(sectionBytes, target)