ultisuite-backend/internal/api/mail/labels.go
2026-05-24 00:03:36 +02:00

116 lines
3.2 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, sort_order, created_at
FROM mail_user_labels
WHERE user_id = (SELECT id FROM users WHERE external_id = $1)
ORDER BY sort_order ASC, 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 sortOrder int
var createdAt any
if err := rows.Scan(&id, &name, &color, &sortOrder, &createdAt); err != nil {
return UserLabelsList{}, err
}
labels = append(labels, map[string]any{
"id": id, "name": name, "color": color, "sort_order": sortOrder, "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, `
WITH next_order AS (
SELECT COALESCE(MAX(sort_order), -10) + 10 AS sort_order
FROM mail_user_labels
WHERE user_id = (SELECT id FROM users WHERE external_id = $1)
)
INSERT INTO mail_user_labels (user_id, name, color, sort_order)
SELECT (SELECT id FROM users WHERE external_id = $1), $2, $3, next_order.sort_order
FROM next_order
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
}