fix: 503 при сбое GetScan вместо ложного 404 scan not found
- Store.GetScan возвращает (job, ok, error); Postgres отдаёт err при ошибке БД/JSON - API: getScanOrWriteError → 503 scan storage temporarily unavailable - Web UI: понятное сообщение для HTTP 503 при опросе скана Made-with: Cursor
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -98,7 +99,7 @@ insert into scan_jobs (
|
||||
return job, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) GetScan(id string) (ScanJob, bool) {
|
||||
func (s *PostgresStore) GetScan(id string) (ScanJob, bool, error) {
|
||||
query := `
|
||||
select id, name, status, cidrs::text, exclude_ips::text, options::text,
|
||||
progress, stats::text, coalesce(snmp_credentials_id::text, ''),
|
||||
@@ -130,28 +131,28 @@ limit 1`
|
||||
&finishedAt,
|
||||
)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return ScanJob{}, false
|
||||
return ScanJob{}, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("postgres GetScan id=%q: row scan: %v", id, err)
|
||||
return ScanJob{}, false
|
||||
return ScanJob{}, false, fmt.Errorf("postgres GetScan id=%q: %w", id, err)
|
||||
}
|
||||
|
||||
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
|
||||
return ScanJob{}, false, fmt.Errorf("postgres GetScan id=%q cidrs: %w", id, err)
|
||||
}
|
||||
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
|
||||
return ScanJob{}, false, fmt.Errorf("postgres GetScan id=%q exclude_ips: %w", id, err)
|
||||
}
|
||||
if err = json.Unmarshal([]byte(optionsRaw), &job.Options); err != nil {
|
||||
log.Printf("postgres GetScan id=%q: options json: %v", id, err)
|
||||
return ScanJob{}, false
|
||||
return ScanJob{}, false, fmt.Errorf("postgres GetScan id=%q options: %w", id, err)
|
||||
}
|
||||
if err = json.Unmarshal([]byte(statsRaw), &job.Stats); err != nil {
|
||||
log.Printf("postgres GetScan id=%q: stats json: %v", id, err)
|
||||
return ScanJob{}, false
|
||||
return ScanJob{}, false, fmt.Errorf("postgres GetScan id=%q stats: %w", id, err)
|
||||
}
|
||||
if startedAt.Valid {
|
||||
job.StartedAt = startedAt.Time
|
||||
@@ -160,7 +161,7 @@ limit 1`
|
||||
job.FinishedAt = finishedAt.Time
|
||||
}
|
||||
|
||||
return job, true
|
||||
return job, true, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) ListScans(limit int) []ScanJob {
|
||||
@@ -230,7 +231,11 @@ limit $1`
|
||||
}
|
||||
|
||||
func (s *PostgresStore) UpdateScan(id string, update ScanUpdate) (ScanJob, bool) {
|
||||
current, ok := s.GetScan(id)
|
||||
current, ok, err := s.GetScan(id)
|
||||
if err != nil {
|
||||
log.Printf("postgres UpdateScan: GetScan id=%q: %v", id, err)
|
||||
return ScanJob{}, false
|
||||
}
|
||||
if !ok {
|
||||
return ScanJob{}, false
|
||||
}
|
||||
|
||||
@@ -88,7 +88,11 @@ func (r *Runner) CancelScan(id string) bool {
|
||||
}
|
||||
|
||||
func (r *Runner) markJobCanceledFromStore(jobID string) {
|
||||
cur, ok := r.store.GetScan(jobID)
|
||||
cur, ok, err := r.store.GetScan(jobID)
|
||||
if err != nil {
|
||||
log.Printf("runner markJobCanceledFromStore GetScan %q: %v", jobID, err)
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@@ -106,7 +110,10 @@ func (r *Runner) markJobCanceledFromStore(jobID string) {
|
||||
}
|
||||
|
||||
func (r *Runner) run(ctx context.Context, job ScanJob) {
|
||||
if cur, ok := r.store.GetScan(job.ID); ok && cur.Status == "canceled" {
|
||||
if cur, ok, err := r.store.GetScan(job.ID); err != nil {
|
||||
log.Printf("runner run GetScan %q: %v", job.ID, err)
|
||||
return
|
||||
} else if ok && cur.Status == "canceled" {
|
||||
return
|
||||
}
|
||||
if err := ctx.Err(); err != nil && errors.Is(err, context.Canceled) {
|
||||
|
||||
@@ -61,7 +61,8 @@ type ScanUpdate struct {
|
||||
|
||||
type Store interface {
|
||||
CreateScan(CreateScanRequest) (ScanJob, error)
|
||||
GetScan(id string) (ScanJob, bool)
|
||||
// GetScan: при found==false и err==nil записи нет; err!=nil — сбой хранилища (например БД).
|
||||
GetScan(id string) (ScanJob, bool, error)
|
||||
ListScans(limit int) []ScanJob
|
||||
UpdateScan(id string, update ScanUpdate) (ScanJob, bool)
|
||||
SaveHostResult(scanID string, host HostResult) error
|
||||
@@ -195,11 +196,11 @@ func (s *MemoryStore) CreateScan(req CreateScanRequest) (ScanJob, error) {
|
||||
return job, nil
|
||||
}
|
||||
|
||||
func (s *MemoryStore) GetScan(id string) (ScanJob, bool) {
|
||||
func (s *MemoryStore) GetScan(id string) (ScanJob, bool, error) {
|
||||
s.mu.RLock()
|
||||
job, ok := s.jobs[id]
|
||||
s.mu.RUnlock()
|
||||
return job, ok
|
||||
return job, ok, nil
|
||||
}
|
||||
|
||||
func (s *MemoryStore) ListScans(limit int) []ScanJob {
|
||||
|
||||
Reference in New Issue
Block a user