126 lines
3.1 KiB
Go
126 lines
3.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"github.com/ultisuite/ulti-backend/internal/api/mail"
|
|
"github.com/ultisuite/ulti-backend/internal/dbmigrate"
|
|
"github.com/ultisuite/ulti-backend/internal/envexpand"
|
|
"github.com/ultisuite/ulti-backend/internal/mail/sanitize"
|
|
)
|
|
|
|
func main() {
|
|
accountID := flag.String("account", "", "mail account UUID (optional; all accounts if empty)")
|
|
dryRun := flag.Bool("dry-run", false, "scan only, do not write updates")
|
|
flag.Parse()
|
|
|
|
for _, path := range []string{".env", "../.env"} {
|
|
_ = envexpand.ApplyFile(path)
|
|
}
|
|
|
|
dbURL := os.Getenv("DATABASE_URL")
|
|
if dbURL == "" {
|
|
slog.Error("DATABASE_URL is required")
|
|
os.Exit(1)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
if err := dbmigrate.Up(dbURL); err != nil {
|
|
slog.Error("migration failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
pool, err := pgxpool.New(ctx, dbURL)
|
|
if err != nil {
|
|
slog.Error("db connect failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
defer pool.Close()
|
|
|
|
if *dryRun {
|
|
scanned, changed, err := scanBodies(ctx, pool, *accountID)
|
|
if err != nil {
|
|
slog.Error("scan failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf("dry-run: scanned=%d would_update=%d\n", scanned, changed)
|
|
return
|
|
}
|
|
|
|
if *accountID != "" {
|
|
svc := mail.NewService(pool, nil, nil, nil, "")
|
|
result, err := svc.ResanitizeAccountBodiesByID(ctx, *accountID)
|
|
if err != nil {
|
|
slog.Error("resanitize failed", "account_id", *accountID, "error", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf("account=%s scanned=%d updated=%d\n", *accountID, result.Scanned, result.Updated)
|
|
return
|
|
}
|
|
|
|
rows, err := pool.Query(ctx, `SELECT id FROM mail_accounts WHERE is_active = true ORDER BY created_at`)
|
|
if err != nil {
|
|
slog.Error("list accounts failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
defer rows.Close()
|
|
|
|
svc := mail.NewService(pool, nil, nil, nil, "")
|
|
var totalScanned, totalUpdated int
|
|
for rows.Next() {
|
|
var id string
|
|
if err := rows.Scan(&id); err != nil {
|
|
slog.Error("scan account id failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
result, err := svc.ResanitizeAccountBodiesByID(ctx, id)
|
|
if err != nil {
|
|
slog.Error("resanitize failed", "account_id", id, "error", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf("account=%s scanned=%d updated=%d\n", id, result.Scanned, result.Updated)
|
|
totalScanned += result.Scanned
|
|
totalUpdated += result.Updated
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
slog.Error("list accounts failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf("done: accounts scanned_messages=%d updated=%d\n", totalScanned, totalUpdated)
|
|
}
|
|
|
|
func scanBodies(ctx context.Context, pool *pgxpool.Pool, accountID string) (scanned, changed int, err error) {
|
|
query := `
|
|
SELECT id, body_html FROM messages
|
|
WHERE body_html <> ''`
|
|
args := []any{}
|
|
if accountID != "" {
|
|
query += ` AND account_id = $1`
|
|
args = append(args, accountID)
|
|
}
|
|
|
|
rows, err := pool.Query(ctx, query, args...)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var id, body string
|
|
if err := rows.Scan(&id, &body); err != nil {
|
|
return scanned, changed, err
|
|
}
|
|
scanned++
|
|
if sanitize.SanitizeHTML(body) != body {
|
|
changed++
|
|
}
|
|
}
|
|
return scanned, changed, rows.Err()
|
|
}
|