ultisuite-backend/internal/api/mail/handlers_search.go
R3D347HR4Y 95196f7777 Add mail attachment and draft management features
- 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.
2026-05-22 17:14:36 +02:00

87 lines
2.4 KiB
Go

package mail
import (
"net/http"
"time"
"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
"github.com/ultisuite/ulti-backend/internal/api/middleware"
"github.com/ultisuite/ulti-backend/internal/api/query"
)
func (h *Handler) SearchMessages(w http.ResponseWriter, r *http.Request) {
claims := middleware.ClaimsFromContext(r.Context())
params, err := query.ParseListRequest(r)
if err != nil {
apivalidate.WriteQueryError(w, r, err)
return
}
filter, verr := parseMessageSearchFilter(r)
if verr != nil {
apivalidate.WriteValidationError(w, r, verr)
return
}
result, err := h.svc.SearchMessages(r.Context(), claims.Sub, filter, params)
if err != nil {
h.logger.Error("search messages", "error", err)
apivalidate.WriteInternal(w, r)
return
}
apiresponse.WriteJSON(w, http.StatusOK, result)
}
func parseMessageSearchFilter(r *http.Request) (MessageSearchFilter, *apivalidate.ValidationError) {
q := r.URL.Query()
filter := MessageSearchFilter{
Query: q.Get("q"),
Sender: q.Get("from"),
Label: q.Get("label"),
AccountID: q.Get("account_id"),
}
if raw := q.Get("date_from"); raw != "" {
t, err := time.Parse(time.RFC3339, raw)
if err != nil {
return filter, apivalidate.NewValidationError(apivalidate.FieldDetail{
Field: "date_from", Message: "invalid RFC3339 datetime",
})
}
filter.DateFrom = &t
}
if raw := q.Get("date_to"); raw != "" {
t, err := time.Parse(time.RFC3339, raw)
if err != nil {
return filter, apivalidate.NewValidationError(apivalidate.FieldDetail{
Field: "date_to", Message: "invalid RFC3339 datetime",
})
}
filter.DateTo = &t
}
if raw := q.Get("has_attachment"); raw != "" {
switch raw {
case "true", "1":
v := true
filter.HasAttachments = &v
case "false", "0":
v := false
filter.HasAttachments = &v
default:
return filter, apivalidate.NewValidationError(apivalidate.FieldDetail{
Field: "has_attachment", Message: "must be true or false",
})
}
}
if filter.Query == "" && filter.Sender == "" && filter.Label == "" &&
filter.AccountID == "" && filter.DateFrom == nil && filter.DateTo == nil &&
filter.HasAttachments == nil {
return filter, apivalidate.NewValidationError(apivalidate.FieldDetail{
Field: "q", Message: "at least one search filter required",
})
}
return filter, nil
}