aboutsummaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/domain/domain.go85
-rw-r--r--services/domain/messages.go3
-rw-r--r--services/domain/tld.go61
-rw-r--r--services/mail/aliases.go76
-rw-r--r--services/mail/mailboxes.go61
-rw-r--r--services/mail/messages.go13
-rw-r--r--services/mail/users.go56
7 files changed, 354 insertions, 1 deletions
diff --git a/services/domain/domain.go b/services/domain/domain.go
index 957bd36..f320189 100644
--- a/services/domain/domain.go
+++ b/services/domain/domain.go
@@ -5,6 +5,7 @@ import (
domainModel "dove/models/domain"
domainRepo "dove/repositories/domain"
+ mailRepo "dove/repositories/mail"
"dove/utils/shortcuts"
"dove/utils/validate"
)
@@ -14,6 +15,16 @@ type CreateDomainRequest struct {
TLDName string `form:"tld_name"`
}
+type UpdateDomainRequest struct {
+ Name string `form:"name"`
+ TLDName string `form:"tld_name"`
+}
+
+type EditDomainFormResponse struct {
+ Domain domainModel.Domain `json:"domain"`
+ TLDs []domainModel.TLD `json:"tlds"`
+}
+
type DomainListResponse struct {
Domains []domainModel.Domain `json:"domains"`
}
@@ -77,3 +88,77 @@ func CreateDomain(request CreateDomainRequest) *shortcuts.Error {
return nil
}
+
+func EditDomainFormData(domainID uint) (*EditDomainFormResponse, *shortcuts.Error) {
+ foundDomain := domainRepo.FindDomainByID(domainID)
+ if foundDomain == nil {
+ return nil, shortcuts.ServiceError(shortcuts.NotFound, DomainNotFound)
+ }
+
+ return &EditDomainFormResponse{
+ Domain: *foundDomain,
+ TLDs: domainRepo.AllTLDs(),
+ }, nil
+}
+
+func UpdateDomain(domainID uint, request UpdateDomainRequest) *shortcuts.Error {
+ foundDomain := domainRepo.FindDomainByID(domainID)
+ if foundDomain == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, DomainNotFound)
+ }
+
+ name := strings.TrimSpace(strings.ToLower(request.Name))
+ tldName := strings.TrimSpace(strings.ToLower(request.TLDName))
+
+ switch {
+ case name == "":
+ return shortcuts.ServiceError(shortcuts.BadRequest, DomainNameRequired)
+ case !validate.DNSLabel(name):
+ return shortcuts.ServiceError(shortcuts.BadRequest, DomainNameInvalid)
+ case tldName == "":
+ return shortcuts.ServiceError(shortcuts.BadRequest, DomainTLDRequired)
+ }
+
+ tld := domainRepo.FindTLDByName(tldName)
+ if tld == nil {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, TLDNotFound)
+ }
+
+ nameChanged := name != foundDomain.Name || tld.ID != foundDomain.TLDID
+
+ if nameChanged {
+ if domainRepo.FindDomainByFullName(name, tldName) != nil {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, DomainAlreadyExists)
+ }
+ }
+
+ foundDomain.Name = name
+ foundDomain.TLDID = tld.ID
+
+ if updateError := domainRepo.UpdateDomain(foundDomain); updateError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, DomainUpdateFailed)
+ }
+
+ if nameChanged {
+ mailRepo.RebuildMailboxAddressesByDomainID(foundDomain.ID)
+ }
+
+ return nil
+}
+
+func DeleteDomain(domainID uint) *shortcuts.Error {
+ foundDomain := domainRepo.FindDomainByID(domainID)
+ if foundDomain == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, DomainNotFound)
+ }
+
+ if mailRepo.CountMailboxesByDomainID(foundDomain.ID) > 0 {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, DomainHasMailboxes)
+ }
+
+ if deleteError := domainRepo.DeleteDomain(foundDomain); deleteError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, DomainDeletionFailed)
+ }
+
+ return nil
+}
diff --git a/services/domain/messages.go b/services/domain/messages.go
index 70be560..583fab0 100644
--- a/services/domain/messages.go
+++ b/services/domain/messages.go
@@ -3,9 +3,12 @@ package domain
const (
DomainAlreadyExists = "A domain with this name already exists under this TLD."
DomainCreationFailed = "Failed to create domain."
+ DomainDeletionFailed = "Failed to delete domain."
+ DomainHasMailboxes = "Cannot delete a domain that has mailboxes. Remove all mailboxes first."
DomainNameInvalid = "Domain name must contain only lowercase letters, numbers, and hyphens."
DomainNameRequired = "Domain name is required."
DomainNotFound = "Domain not found."
+ DomainUpdateFailed = "Failed to update domain."
DomainTLDRequired = "A TLD must be selected for the domain."
TLDAlreadyExists = "A TLD with this name already exists."
TLDCreationFailed = "Failed to create TLD."
diff --git a/services/domain/tld.go b/services/domain/tld.go
index d9140b7..2cb7c05 100644
--- a/services/domain/tld.go
+++ b/services/domain/tld.go
@@ -5,6 +5,7 @@ import (
domainModel "dove/models/domain"
domainRepo "dove/repositories/domain"
+ mailRepo "dove/repositories/mail"
"dove/utils/shortcuts"
"dove/utils/validate"
)
@@ -13,6 +14,14 @@ type CreateTLDRequest struct {
Name string `form:"name"`
}
+type UpdateTLDRequest struct {
+ Name string `form:"name"`
+}
+
+type EditTLDFormResponse struct {
+ TLD domainModel.TLD `json:"tld"`
+}
+
func AllTLDs() []domainModel.TLD {
return domainRepo.AllTLDs()
}
@@ -41,6 +50,58 @@ func CreateTLD(request CreateTLDRequest) *shortcuts.Error {
return nil
}
+func EditTLDFormData(tldID uint) (*EditTLDFormResponse, *shortcuts.Error) {
+ tld := domainRepo.FindTLDByID(tldID)
+ if tld == nil {
+ return nil, shortcuts.ServiceError(shortcuts.NotFound, TLDNotFound)
+ }
+
+ if tld.IsDefault {
+ return nil, shortcuts.ServiceError(shortcuts.Forbidden, TLDProtected)
+ }
+
+ return &EditTLDFormResponse{TLD: *tld}, nil
+}
+
+func UpdateTLD(tldID uint, request UpdateTLDRequest) *shortcuts.Error {
+ tld := domainRepo.FindTLDByID(tldID)
+ if tld == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, TLDNotFound)
+ }
+
+ if tld.IsDefault {
+ return shortcuts.ServiceError(shortcuts.Forbidden, TLDProtected)
+ }
+
+ newName := strings.TrimSpace(strings.ToLower(request.Name))
+
+ switch {
+ case newName == "":
+ return shortcuts.ServiceError(shortcuts.BadRequest, TLDNameRequired)
+ case !validate.DNSLabel(newName):
+ return shortcuts.ServiceError(shortcuts.BadRequest, TLDNameInvalid)
+ }
+
+ if newName != tld.Name {
+ if domainRepo.FindTLDByName(newName) != nil {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, TLDAlreadyExists)
+ }
+ }
+
+ tld.Name = newName
+
+ if updateError := domainRepo.UpdateTLD(tld); updateError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, TLDUpdateFailed)
+ }
+
+ domains := domainRepo.FindDomainsByTLDID(tld.ID)
+ for _, domain := range domains {
+ mailRepo.RebuildMailboxAddressesByDomainID(domain.ID)
+ }
+
+ return nil
+}
+
func DeleteTLD(name string) *shortcuts.Error {
tld := domainRepo.FindTLDByName(name)
diff --git a/services/mail/aliases.go b/services/mail/aliases.go
new file mode 100644
index 0000000..2999681
--- /dev/null
+++ b/services/mail/aliases.go
@@ -0,0 +1,76 @@
+package mail
+
+import (
+ "strings"
+
+ domainRepo "dove/repositories/domain"
+ mailModel "dove/models/mail"
+ mailRepo "dove/repositories/mail"
+ "dove/utils/shortcuts"
+ "dove/utils/validate"
+)
+
+type CreateAliasRequest struct {
+ LocalPart string `form:"local_part"`
+ DomainID uint `form:"domain_id"`
+ MailboxID uint
+}
+
+func CreateAlias(request CreateAliasRequest) *shortcuts.Error {
+ localPart := strings.TrimSpace(request.LocalPart)
+
+ switch {
+ case localPart == "":
+ return shortcuts.ServiceError(shortcuts.BadRequest, LocalPartRequired)
+ case !validate.EmailLocalPart(localPart):
+ return shortcuts.ServiceError(shortcuts.BadRequest, LocalPartInvalid)
+ case request.DomainID == 0:
+ return shortcuts.ServiceError(shortcuts.BadRequest, DomainRequired)
+ case request.MailboxID == 0:
+ return shortcuts.ServiceError(shortcuts.BadRequest, MailboxNotFound)
+ }
+
+ mailbox := mailRepo.FindMailboxByID(request.MailboxID)
+ if mailbox == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, MailboxNotFound)
+ }
+
+ foundDomain := domainRepo.FindDomainByID(request.DomainID)
+ if foundDomain == nil {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, DomainNotFound)
+ }
+
+ sourceAddress := localPart + "@" + foundDomain.Name + "." + foundDomain.TLD.Name
+
+ if mailRepo.FindMailboxByAddress(sourceAddress) != nil {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, AliasAlreadyExists)
+ }
+
+ if mailRepo.FindAliasByAddress(sourceAddress) != nil {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, AliasAlreadyExists)
+ }
+
+ alias := &mailModel.Alias{
+ SourceAddress: sourceAddress,
+ MailboxID: request.MailboxID,
+ }
+
+ if createError := mailRepo.CreateAlias(alias); createError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, AliasCreationFailed)
+ }
+
+ return nil
+}
+
+func DeleteAlias(aliasID uint) *shortcuts.Error {
+ alias := mailRepo.FindAliasByID(aliasID)
+ if alias == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, AliasNotFound)
+ }
+
+ if deleteError := mailRepo.DeleteAlias(alias); deleteError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, AliasDeletionFailed)
+ }
+
+ return nil
+}
diff --git a/services/mail/mailboxes.go b/services/mail/mailboxes.go
index 041c64b..811e6bc 100644
--- a/services/mail/mailboxes.go
+++ b/services/mail/mailboxes.go
@@ -18,11 +18,22 @@ type CreateMailboxRequest struct {
UserID uint `form:"user_id"`
}
+type UpdateMailboxRequest struct {
+ UserID uint `form:"user_id"`
+}
+
type MailboxFormResponse struct {
Users []mailModel.User `json:"users"`
Domains []domainModel.Domain `json:"domains"`
}
+type EditMailboxFormResponse struct {
+ Mailbox mailModel.Mailbox `json:"mailbox"`
+ Users []mailModel.User `json:"users"`
+ Aliases []mailModel.Alias `json:"aliases"`
+ Domains []domainModel.Domain `json:"domains"`
+}
+
type MailboxView struct {
Address string
}
@@ -80,3 +91,53 @@ func CreateMailbox(request CreateMailboxRequest) *shortcuts.Error {
return nil
}
+
+func EditMailboxFormData(mailboxID uint) (*EditMailboxFormResponse, *shortcuts.Error) {
+ mailbox := mailRepo.FindMailboxByIDWithRelations(mailboxID)
+ if mailbox == nil {
+ return nil, shortcuts.ServiceError(shortcuts.NotFound, MailboxNotFound)
+ }
+
+ return &EditMailboxFormResponse{
+ Mailbox: *mailbox,
+ Users: mailRepo.AllUsers(),
+ Aliases: mailRepo.FindAliasesByMailboxID(mailboxID),
+ Domains: domainRepo.AllDomains(),
+ }, nil
+}
+
+func UpdateMailbox(mailboxID uint, request UpdateMailboxRequest) *shortcuts.Error {
+ mailbox := mailRepo.FindMailboxByID(mailboxID)
+ if mailbox == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, MailboxNotFound)
+ }
+
+ if request.UserID == 0 {
+ return shortcuts.ServiceError(shortcuts.BadRequest, UserRequired)
+ }
+
+ if mailRepo.FindUserByID(request.UserID) == nil {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, UserNotFound)
+ }
+
+ mailbox.UserID = request.UserID
+
+ if updateError := mailRepo.UpdateMailbox(mailbox); updateError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, MailboxUpdateFailed)
+ }
+
+ return nil
+}
+
+func DeleteMailbox(mailboxID uint) *shortcuts.Error {
+ mailbox := mailRepo.FindMailboxByID(mailboxID)
+ if mailbox == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, MailboxNotFound)
+ }
+
+ if deleteError := mailRepo.DeleteMailbox(mailbox); deleteError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, DeletionFailed)
+ }
+
+ return nil
+}
diff --git a/services/mail/messages.go b/services/mail/messages.go
index 13839e4..118b630 100644
--- a/services/mail/messages.go
+++ b/services/mail/messages.go
@@ -1,17 +1,30 @@
package mail
const (
+ AliasAddressRequired = "Alias address is required."
+ AliasAlreadyExists = "An alias with this address already exists."
+ AliasCreationFailed = "Failed to create alias."
+ AliasDeletionFailed = "Failed to delete alias."
+ AliasInvalid = "Alias address must be a valid email format ([email protected])."
+ AliasNotFound = "Alias not found."
+
LocalPartInvalid = "Local part must contain only lowercase letters, numbers, dots, hyphens, and underscores."
LocalPartRequired = "The local part of the address is required."
DomainRequired = "A domain must be selected for the mailbox."
DomainNotFound = "The selected domain does not exist."
AlreadyExists = "A mailbox with this address already exists."
CreationFailed = "Failed to create mailbox."
+ DeletionFailed = "Failed to delete mailbox."
+ MailboxNotFound = "Mailbox not found."
+ MailboxUpdateFailed = "Failed to update mailbox."
UserNotFound = "The selected user does not exist."
UserRequired = "A user must be selected for the mailbox."
DisplayNameRequired = "Display name is required."
UserAlreadyExists = "A user with this username already exists."
UserCreationFailed = "Failed to create user."
+ UserDeletionFailed = "Failed to delete user."
+ UserHasMailboxes = "Cannot delete a user that has mailboxes. Remove all mailboxes first."
+ UserUpdateFailed = "Failed to update user."
UsernameRequired = "Username is required."
)
diff --git a/services/mail/users.go b/services/mail/users.go
index 9c776a1..025c411 100644
--- a/services/mail/users.go
+++ b/services/mail/users.go
@@ -14,6 +14,14 @@ type CreateUserRequest struct {
DisplayName string `form:"display_name"`
}
+type UpdateUserRequest struct {
+ DisplayName string `form:"display_name"`
+}
+
+type EditUserFormResponse struct {
+ User mailModel.User `json:"user"`
+}
+
func ListUsers(pagination meta.Pagination, sorting meta.Sorting, search string) meta.PaginatedResponse {
users, total := mailRepo.ListUsers(pagination, sorting, search)
return pagination.Response(users, total)
@@ -47,6 +55,52 @@ func CreateUser(request CreateUserRequest) *shortcuts.Error {
return nil
}
+func EditUserFormData(userID uint) (*EditUserFormResponse, *shortcuts.Error) {
+ user := mailRepo.FindUserByID(userID)
+ if user == nil {
+ return nil, shortcuts.ServiceError(shortcuts.NotFound, UserNotFound)
+ }
+
+ return &EditUserFormResponse{User: *user}, nil
+}
+
+func UpdateUser(userID uint, request UpdateUserRequest) *shortcuts.Error {
+ user := mailRepo.FindUserByID(userID)
+ if user == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, UserNotFound)
+ }
+
+ displayName := strings.TrimSpace(request.DisplayName)
+ if displayName == "" {
+ return shortcuts.ServiceError(shortcuts.BadRequest, DisplayNameRequired)
+ }
+
+ user.DisplayName = displayName
+
+ if updateError := mailRepo.UpdateUser(user); updateError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, UserUpdateFailed)
+ }
+
+ return nil
+}
+
+func DeleteUser(userID uint) *shortcuts.Error {
+ user := mailRepo.FindUserByID(userID)
+ if user == nil {
+ return shortcuts.ServiceError(shortcuts.NotFound, UserNotFound)
+ }
+
+ if mailRepo.CountMailboxesByUserID(user.ID) > 0 {
+ return shortcuts.ServiceError(shortcuts.Unprocessable, UserHasMailboxes)
+ }
+
+ if deleteError := mailRepo.DeleteUser(user); deleteError != nil {
+ return shortcuts.ServiceError(shortcuts.Internal, UserDeletionFailed)
+ }
+
+ return nil
+}
+
func AllUsers() []mailModel.User {
return mailRepo.AllUsers()
-}
+} \ No newline at end of file