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:
+37
-29
@@ -14,6 +14,9 @@ import (
|
||||
"nettopo-go/internal/webui"
|
||||
)
|
||||
|
||||
// errScanStorageUnavailable — ответ API при сбое GetScan (БД, а не «нет такого scan_id»).
|
||||
const errScanStorageUnavailable = "scan storage temporarily unavailable"
|
||||
|
||||
type Handler struct {
|
||||
store scans.Store
|
||||
run *scans.Runner
|
||||
@@ -83,6 +86,26 @@ func scanPathID(r *http.Request) string {
|
||||
return strings.TrimSpace(raw)
|
||||
}
|
||||
|
||||
// getScanOrWriteError загружает скан; при ошибке хранилища пишет 503, при отсутствии строки — 404 с notFoundMsg (или «scan not found»).
|
||||
func (h *Handler) getScanOrWriteError(w http.ResponseWriter, id, notFoundMsg string) (scans.ScanJob, bool) {
|
||||
job, found, err := h.store.GetScan(id)
|
||||
if err != nil {
|
||||
log.Printf("GetScan id=%q (len=%d): %v", id, len(id), err)
|
||||
writeError(w, http.StatusServiceUnavailable, errScanStorageUnavailable)
|
||||
return scans.ScanJob{}, false
|
||||
}
|
||||
if !found {
|
||||
msg := notFoundMsg
|
||||
if msg == "" {
|
||||
msg = "scan not found"
|
||||
}
|
||||
log.Printf("getScan: not found id=%q (len=%d) msg=%q", id, len(id), msg)
|
||||
writeError(w, http.StatusNotFound, msg)
|
||||
return scans.ScanJob{}, false
|
||||
}
|
||||
return job, true
|
||||
}
|
||||
|
||||
func (h *Handler) Routes() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
webFS := http.FileServer(http.FS(webui.FS))
|
||||
@@ -189,12 +212,10 @@ func (h *Handler) getScansDiff(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "both from and to are required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(fromID); !ok {
|
||||
writeError(w, http.StatusNotFound, "from scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, fromID, "from scan not found"); !ok {
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(toID); !ok {
|
||||
writeError(w, http.StatusNotFound, "to scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, toID, "to scan not found"); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -221,10 +242,8 @@ func (h *Handler) getScan(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
job, ok := h.store.GetScan(id)
|
||||
job, ok := h.getScanOrWriteError(w, id, "")
|
||||
if !ok {
|
||||
log.Printf("getScan: not found id=%q (len=%d)", id, len(id))
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -241,9 +260,8 @@ func (h *Handler) postCancelScan(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
log.Printf("postCancelScan: not found id=%q (len=%d)", id, len(id))
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
cur, ok := h.getScanOrWriteError(w, id, "")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
runnerSignaled := false
|
||||
@@ -254,7 +272,6 @@ func (h *Handler) postCancelScan(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "runner_canceled": true})
|
||||
return
|
||||
}
|
||||
cur, _ := h.store.GetScan(id)
|
||||
if cur.Status == "queued" {
|
||||
fin := time.Now().UTC()
|
||||
_, _ = h.store.UpdateScan(id, scans.ScanUpdate{Status: "canceled", FinishedAt: &fin})
|
||||
@@ -270,8 +287,7 @@ func (h *Handler) getScanHosts(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -296,8 +312,7 @@ func (h *Handler) getScanPorts(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -322,8 +337,7 @@ func (h *Handler) getScanSNMP(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -348,8 +362,7 @@ func (h *Handler) getScanLLDP(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -374,8 +387,7 @@ func (h *Handler) getScanInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
ip := strings.TrimSpace(r.URL.Query().Get("ip"))
|
||||
@@ -403,8 +415,7 @@ func (h *Handler) getScanPortDevices(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
ip := strings.TrimSpace(r.URL.Query().Get("ip"))
|
||||
@@ -463,8 +474,7 @@ func (h *Handler) getScanMacLocations(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
raw := strings.TrimSpace(r.URL.Query().Get("mac"))
|
||||
@@ -612,8 +622,7 @@ func (h *Handler) getScanLinks(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -675,8 +684,7 @@ func (h *Handler) getScanTopology(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusBadRequest, "scan id is required")
|
||||
return
|
||||
}
|
||||
if _, ok := h.store.GetScan(id); !ok {
|
||||
writeError(w, http.StatusNotFound, "scan not found")
|
||||
if _, ok := h.getScanOrWriteError(w, id, ""); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user