ultisuite-backend/internal/api/mail/labels.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

109 lines
2.9 KiB
Go

package mail
import (
"context"
"github.com/ultisuite/ulti-backend/internal/api/query"
"github.com/ultisuite/ulti-backend/internal/securityaudit"
)
type UserLabelsList struct {
Labels []map[string]any `json:"labels"`
Pagination query.PaginationMeta `json:"pagination,omitempty"`
}
func (s *Service) ListUserLabels(ctx context.Context, externalID string, params query.ListParams) (UserLabelsList, error) {
var total int64
err := s.db.QueryRow(ctx, `
SELECT COUNT(*) FROM mail_user_labels
WHERE user_id = (SELECT id FROM users WHERE external_id = $1)
`, externalID).Scan(&total)
if err != nil {
return UserLabelsList{}, err
}
rows, err := s.db.Query(ctx, `
SELECT id, name, color, created_at
FROM mail_user_labels
WHERE user_id = (SELECT id FROM users WHERE external_id = $1)
ORDER BY name ASC
LIMIT $2 OFFSET $3
`, externalID, params.Limit(), params.Offset())
if err != nil {
return UserLabelsList{}, err
}
defer rows.Close()
labels := make([]map[string]any, 0)
for rows.Next() {
var id, name, color string
var createdAt any
if err := rows.Scan(&id, &name, &color, &createdAt); err != nil {
return UserLabelsList{}, err
}
labels = append(labels, map[string]any{
"id": id, "name": name, "color": color, "created_at": createdAt,
})
}
if err := rows.Err(); err != nil {
return UserLabelsList{}, err
}
return UserLabelsList{
Labels: labels,
Pagination: params.Meta(&total),
}, nil
}
func (s *Service) CreateUserLabel(ctx context.Context, externalID string, req *createUserLabelRequest) (string, error) {
var id string
err := s.db.QueryRow(ctx, `
INSERT INTO mail_user_labels (user_id, name, color)
VALUES ((SELECT id FROM users WHERE external_id = $1), $2, $3)
RETURNING id
`, externalID, req.Name, req.Color).Scan(&id)
if err != nil {
if isUniqueViolation(err) {
return "", ErrDuplicateLabel
}
return "", err
}
return id, nil
}
func (s *Service) UpdateUserLabel(ctx context.Context, externalID, labelID string, req *updateUserLabelRequest) error {
result, err := s.db.Exec(ctx, `
UPDATE mail_user_labels SET name = $1, color = $2
WHERE id = $3 AND user_id = (SELECT id FROM users WHERE external_id = $4)
`, req.Name, req.Color, labelID, externalID)
if err != nil {
if isUniqueViolation(err) {
return ErrDuplicateLabel
}
return err
}
if result.RowsAffected() == 0 {
return ErrNotFound
}
return nil
}
func (s *Service) DeleteUserLabel(ctx context.Context, externalID, labelID string) error {
result, err := s.db.Exec(ctx, `
DELETE FROM mail_user_labels
WHERE id = $1 AND user_id = (SELECT id FROM users WHERE external_id = $2)
`, labelID, externalID)
if err != nil {
return err
}
if result.RowsAffected() == 0 {
return ErrNotFound
}
if s.audit != nil {
s.audit.Log(ctx, externalID, securityaudit.ActionCriticalDeletion, map[string]any{
"target": "mail_user_label", "label_id": labelID,
})
}
return nil
}