- Introduced new functionality for managing email attachments and drafts in the mail API. - Added handlers for listing, uploading, and downloading message attachments in `internal/api/mail/handlers_attachments.go`. - Implemented draft management endpoints for creating, updating, and deleting drafts in `internal/api/mail/handlers_drafts.go`. - Created new service methods for handling draft and attachment operations in `internal/api/mail/drafts.go` and `internal/api/mail/storage.go`. - Added validation and error handling for draft and attachment operations. - Included unit tests for draft and folder functionalities in `internal/api/mail/drafts_test.go` and `internal/api/mail/folders_test.go`. - Updated API routes to support new draft and attachment features, enhancing overall mail management capabilities.
193 lines
5.1 KiB
Go
193 lines
5.1 KiB
Go
package mail
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
|
|
)
|
|
|
|
const (
|
|
maxFoldersRequestBody = 32 << 10 // 32 KiB
|
|
maxLabelsRequestBody = 8 << 10 // 8 KiB
|
|
maxFolderName = 256
|
|
maxRemoteName = 512
|
|
maxLabelName = 128
|
|
maxLabelColor = 32
|
|
)
|
|
|
|
var allowedFolderTypes = map[string]struct{}{
|
|
"inbox": {},
|
|
"sent": {},
|
|
"drafts": {},
|
|
"trash": {},
|
|
"archive": {},
|
|
"spam": {},
|
|
"custom": {},
|
|
}
|
|
|
|
type createFolderRequest struct {
|
|
AccountID string `json:"account_id"`
|
|
Name string `json:"name"`
|
|
RemoteName string `json:"remote_name"`
|
|
FolderType string `json:"folder_type"`
|
|
}
|
|
|
|
type updateFolderRequest struct {
|
|
Name string `json:"name"`
|
|
RemoteName string `json:"remote_name"`
|
|
FolderType string `json:"folder_type"`
|
|
}
|
|
|
|
type createUserLabelRequest struct {
|
|
Name string `json:"name"`
|
|
Color string `json:"color"`
|
|
}
|
|
|
|
type updateUserLabelRequest struct {
|
|
Name string `json:"name"`
|
|
Color string `json:"color"`
|
|
}
|
|
|
|
func validateFolderType(field, folderType string) *apivalidate.FieldDetail {
|
|
folderType = strings.TrimSpace(strings.ToLower(folderType))
|
|
if folderType == "" {
|
|
return nil
|
|
}
|
|
if _, ok := allowedFolderTypes[folderType]; !ok {
|
|
return &apivalidate.FieldDetail{Field: field, Message: "invalid"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func normalizeFolderType(folderType string) string {
|
|
folderType = strings.TrimSpace(strings.ToLower(folderType))
|
|
if folderType == "" {
|
|
return "custom"
|
|
}
|
|
return folderType
|
|
}
|
|
|
|
func validateFolderName(field, name string, required bool) *apivalidate.FieldDetail {
|
|
name = strings.TrimSpace(name)
|
|
if name == "" {
|
|
if required {
|
|
return &apivalidate.FieldDetail{Field: field, Message: "required"}
|
|
}
|
|
return nil
|
|
}
|
|
if len(name) > maxFolderName || containsNewline(name) {
|
|
return &apivalidate.FieldDetail{Field: field, Message: "invalid"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateRemoteName(field, remoteName string, required bool) *apivalidate.FieldDetail {
|
|
remoteName = strings.TrimSpace(remoteName)
|
|
if remoteName == "" {
|
|
if required {
|
|
return &apivalidate.FieldDetail{Field: field, Message: "required"}
|
|
}
|
|
return nil
|
|
}
|
|
if len(remoteName) > maxRemoteName || containsNewline(remoteName) {
|
|
return &apivalidate.FieldDetail{Field: field, Message: "invalid"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateCreateFolder(req *createFolderRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if strings.TrimSpace(req.AccountID) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "account_id", Message: "required"})
|
|
}
|
|
if d := validateFolderName("name", req.Name, true); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if d := validateRemoteName("remote_name", req.RemoteName, false); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if d := validateFolderType("folder_type", req.FolderType); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if len(details) == 0 {
|
|
return nil
|
|
}
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
|
|
func validateUpdateFolder(req *updateFolderRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if d := validateFolderName("name", req.Name, true); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if d := validateRemoteName("remote_name", req.RemoteName, true); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if d := validateFolderType("folder_type", req.FolderType); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if len(details) == 0 {
|
|
return nil
|
|
}
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
|
|
func validateLabelName(field, name string, required bool) *apivalidate.FieldDetail {
|
|
name = strings.TrimSpace(name)
|
|
if name == "" {
|
|
if required {
|
|
return &apivalidate.FieldDetail{Field: field, Message: "required"}
|
|
}
|
|
return nil
|
|
}
|
|
if len(name) > maxLabelName || containsNewline(name) {
|
|
return &apivalidate.FieldDetail{Field: field, Message: "invalid"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateLabelColor(color string) *apivalidate.FieldDetail {
|
|
color = strings.TrimSpace(color)
|
|
if len(color) > maxLabelColor || containsNewline(color) {
|
|
return &apivalidate.FieldDetail{Field: "color", Message: "invalid"}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateCreateUserLabel(req *createUserLabelRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if d := validateLabelName("name", req.Name, true); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if d := validateLabelColor(req.Color); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if len(details) == 0 {
|
|
return nil
|
|
}
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
|
|
func validateUpdateUserLabel(req *updateUserLabelRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if d := validateLabelName("name", req.Name, true); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if d := validateLabelColor(req.Color); d != nil {
|
|
details = append(details, *d)
|
|
}
|
|
if len(details) == 0 {
|
|
return nil
|
|
}
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
|
|
func validateListFoldersAccountID(accountID string) *apivalidate.ValidationError {
|
|
if strings.TrimSpace(accountID) == "" {
|
|
return apivalidate.NewValidationError(apivalidate.FieldDetail{
|
|
Field: "account_id", Message: "required",
|
|
})
|
|
}
|
|
return nil
|
|
}
|