package api import ( "encoding/json" "net/http" "strings" "time" "nettopo-go/internal/scans" ) type Handler struct { store scans.Store } func NewHandler(store scans.Store) *Handler { return &Handler{store: store} } func (h *Handler) Routes() http.Handler { mux := http.NewServeMux() mux.HandleFunc("GET /health", h.health) mux.HandleFunc("POST /api/scans", h.createScan) mux.HandleFunc("GET /api/scans/{id}", h.getScan) return withJSON(mux) } func (h *Handler) health(w http.ResponseWriter, _ *http.Request) { writeJSON(w, http.StatusOK, map[string]any{ "status": "ok", "time": time.Now().UTC(), }) } func (h *Handler) createScan(w http.ResponseWriter, r *http.Request) { var req scans.CreateScanRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid json body") return } req.Name = strings.TrimSpace(req.Name) if req.Name == "" { req.Name = "manual-scan" } job, err := h.store.CreateScan(req) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } writeJSON(w, http.StatusCreated, map[string]any{ "scan_id": job.ID, "status": job.Status, }) } func (h *Handler) getScan(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") if id == "" { writeError(w, http.StatusBadRequest, "scan id is required") return } job, ok := h.store.GetScan(id) if !ok { writeError(w, http.StatusNotFound, "scan not found") return } writeJSON(w, http.StatusOK, job) } func withJSON(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") next.ServeHTTP(w, r) }) } func writeJSON(w http.ResponseWriter, status int, payload any) { w.WriteHeader(status) _ = json.NewEncoder(w).Encode(payload) } func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, map[string]string{"error": message}) }