feat: отмена скана на сервере, стабильнее scan_id, упрощённый UI

- Runner: context, POST /api/scans/{id}/cancel, частичный canceled; Start до ответа createScan

- API: scanPathID (PathUnescape), лог при 404; Postgres GetScan логирует ошибки чтения

- newScanID: суффикс 8 hex; тест обновлён

- Web UI: убраны строки scan_id/from-to; скрытое поле scanId; cancel при таймауте/новом скане

Made-with: Cursor
This commit is contained in:
PTah
2026-04-13 15:26:23 +10:00
parent 5b3eeaed45
commit 088be32ec5
6 changed files with 224 additions and 210 deletions
+6
View File
@@ -5,6 +5,7 @@ import (
"database/sql"
"encoding/json"
"errors"
"log"
"sort"
"strings"
"time"
@@ -132,19 +133,24 @@ limit 1`
return ScanJob{}, false
}
if err != nil {
log.Printf("postgres GetScan id=%q: row scan: %v", id, err)
return ScanJob{}, false
}
if err = json.Unmarshal([]byte(cidrsRaw), &job.CIDRs); err != nil {
log.Printf("postgres GetScan id=%q: cidrs json (len=%d): %v", id, len(cidrsRaw), err)
return ScanJob{}, false
}
if err = json.Unmarshal([]byte(excludeRaw), &job.ExcludeIPs); err != nil {
log.Printf("postgres GetScan id=%q: exclude_ips json: %v", id, err)
return ScanJob{}, false
}
if err = json.Unmarshal([]byte(optionsRaw), &job.Options); err != nil {
log.Printf("postgres GetScan id=%q: options json: %v", id, err)
return ScanJob{}, false
}
if err = json.Unmarshal([]byte(statsRaw), &job.Stats); err != nil {
log.Printf("postgres GetScan id=%q: stats json: %v", id, err)
return ScanJob{}, false
}
if startedAt.Valid {