ultisuite-backend/internal/api/mail/resanitize_bodies.go
R3D347HR4Y cd0a80f5e8 huhu
2026-05-25 13:52:27 +02:00

77 lines
1.8 KiB
Go

package mail
import (
"context"
"github.com/ultisuite/ulti-backend/internal/mail/sanitize"
)
const resanitizeBatchSize = 200
type ResanitizeBodiesResult struct {
Scanned int `json:"scanned"`
Updated int `json:"updated"`
}
// ResanitizeAccountBodies re-applies email HTML sanitization to stored messages.
func (s *Service) ResanitizeAccountBodies(ctx context.Context, externalID, accountID string) (ResanitizeBodiesResult, error) {
if err := s.verifyAccountOwnership(ctx, externalID, accountID); err != nil {
return ResanitizeBodiesResult{}, err
}
return s.ResanitizeAccountBodiesByID(ctx, accountID)
}
// ResanitizeAccountBodiesByID re-sanitizes messages without an ownership check (CLI/admin).
func (s *Service) ResanitizeAccountBodiesByID(ctx context.Context, accountID string) (ResanitizeBodiesResult, error) {
var result ResanitizeBodiesResult
var lastID string
for {
rows, err := s.db.Query(ctx, `
SELECT id, body_html
FROM messages
WHERE account_id = $1
AND body_html <> ''
AND ($2 = '' OR id > $2::uuid)
ORDER BY id
LIMIT $3
`, accountID, lastID, resanitizeBatchSize)
if err != nil {
return result, err
}
batchCount := 0
for rows.Next() {
var id, bodyHTML string
if err := rows.Scan(&id, &bodyHTML); err != nil {
rows.Close()
return result, err
}
batchCount++
result.Scanned++
lastID = id
sanitized := sanitize.SanitizeHTML(bodyHTML)
if sanitized == bodyHTML {
continue
}
if _, err := s.db.Exec(ctx, `
UPDATE messages SET body_html = $2, updated_at = NOW() WHERE id = $1
`, id, sanitized); err != nil {
rows.Close()
return result, err
}
result.Updated++
}
if err := rows.Err(); err != nil {
return result, err
}
rows.Close()
if batchCount < resanitizeBatchSize {
break
}
}
return result, nil
}