diff options
Diffstat (limited to 'services')
| -rw-r--r-- | services/domain/domain.go | 85 | ||||
| -rw-r--r-- | services/domain/messages.go | 3 | ||||
| -rw-r--r-- | services/domain/tld.go | 61 | ||||
| -rw-r--r-- | services/mail/aliases.go | 76 | ||||
| -rw-r--r-- | services/mail/mailboxes.go | 61 | ||||
| -rw-r--r-- | services/mail/messages.go | 13 | ||||
| -rw-r--r-- | services/mail/users.go | 56 |
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 |
