mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:51:31 +00:00
4f90be9311
M21 adds server-side active TLS scanning of CIDR ranges with concurrent probing, sentinel agent pattern for pipeline reuse, and full CRUD API for scan targets. M22 adds Prometheus exposition format endpoint alongside existing JSON metrics. Comprehensive documentation audit updates all docs to reflect 91 endpoints, 19 tables, 6 scheduler loops, and 900+ tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
180 lines
5.1 KiB
Go
180 lines
5.1 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/shankar0123/certctl/internal/domain"
|
|
)
|
|
|
|
// NetworkScanService defines the interface used by the network scan handler.
|
|
type NetworkScanService interface {
|
|
ListTargets(ctx context.Context) ([]*domain.NetworkScanTarget, error)
|
|
GetTarget(ctx context.Context, id string) (*domain.NetworkScanTarget, error)
|
|
CreateTarget(ctx context.Context, target *domain.NetworkScanTarget) (*domain.NetworkScanTarget, error)
|
|
UpdateTarget(ctx context.Context, id string, target *domain.NetworkScanTarget) (*domain.NetworkScanTarget, error)
|
|
DeleteTarget(ctx context.Context, id string) error
|
|
TriggerScan(ctx context.Context, targetID string) (*domain.DiscoveryScan, error)
|
|
}
|
|
|
|
// NetworkScanHandler handles HTTP requests for network scan targets.
|
|
type NetworkScanHandler struct {
|
|
svc NetworkScanService
|
|
}
|
|
|
|
// NewNetworkScanHandler creates a new network scan handler.
|
|
func NewNetworkScanHandler(svc NetworkScanService) NetworkScanHandler {
|
|
return NetworkScanHandler{svc: svc}
|
|
}
|
|
|
|
// ListNetworkScanTargets handles GET /api/v1/network-scan-targets
|
|
func (h NetworkScanHandler) ListNetworkScanTargets(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
Error(w, http.StatusMethodNotAllowed, "Method not allowed")
|
|
return
|
|
}
|
|
|
|
targets, err := h.svc.ListTargets(r.Context())
|
|
if err != nil {
|
|
Error(w, http.StatusInternalServerError, fmt.Sprintf("failed to list network scan targets: %v", err))
|
|
return
|
|
}
|
|
|
|
if targets == nil {
|
|
targets = []*domain.NetworkScanTarget{}
|
|
}
|
|
|
|
JSON(w, http.StatusOK, PagedResponse{
|
|
Data: targets,
|
|
Total: int64(len(targets)),
|
|
Page: 1,
|
|
PerPage: len(targets),
|
|
})
|
|
}
|
|
|
|
// GetNetworkScanTarget handles GET /api/v1/network-scan-targets/{id}
|
|
func (h NetworkScanHandler) GetNetworkScanTarget(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
Error(w, http.StatusMethodNotAllowed, "Method not allowed")
|
|
return
|
|
}
|
|
|
|
id := r.PathValue("id")
|
|
if id == "" {
|
|
Error(w, http.StatusBadRequest, "network scan target ID is required")
|
|
return
|
|
}
|
|
|
|
target, err := h.svc.GetTarget(r.Context(), id)
|
|
if err != nil {
|
|
Error(w, http.StatusNotFound, fmt.Sprintf("network scan target not found: %v", err))
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusOK, target)
|
|
}
|
|
|
|
// CreateNetworkScanTarget handles POST /api/v1/network-scan-targets
|
|
func (h NetworkScanHandler) CreateNetworkScanTarget(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
Error(w, http.StatusMethodNotAllowed, "Method not allowed")
|
|
return
|
|
}
|
|
|
|
var target domain.NetworkScanTarget
|
|
if err := json.NewDecoder(r.Body).Decode(&target); err != nil {
|
|
Error(w, http.StatusBadRequest, fmt.Sprintf("invalid request body: %v", err))
|
|
return
|
|
}
|
|
|
|
created, err := h.svc.CreateTarget(r.Context(), &target)
|
|
if err != nil {
|
|
Error(w, http.StatusBadRequest, fmt.Sprintf("failed to create network scan target: %v", err))
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusCreated, created)
|
|
}
|
|
|
|
// UpdateNetworkScanTarget handles PUT /api/v1/network-scan-targets/{id}
|
|
func (h NetworkScanHandler) UpdateNetworkScanTarget(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPut {
|
|
Error(w, http.StatusMethodNotAllowed, "Method not allowed")
|
|
return
|
|
}
|
|
|
|
id := r.PathValue("id")
|
|
if id == "" {
|
|
Error(w, http.StatusBadRequest, "network scan target ID is required")
|
|
return
|
|
}
|
|
|
|
var target domain.NetworkScanTarget
|
|
if err := json.NewDecoder(r.Body).Decode(&target); err != nil {
|
|
Error(w, http.StatusBadRequest, fmt.Sprintf("invalid request body: %v", err))
|
|
return
|
|
}
|
|
|
|
updated, err := h.svc.UpdateTarget(r.Context(), id, &target)
|
|
if err != nil {
|
|
Error(w, http.StatusInternalServerError, fmt.Sprintf("failed to update network scan target: %v", err))
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusOK, updated)
|
|
}
|
|
|
|
// DeleteNetworkScanTarget handles DELETE /api/v1/network-scan-targets/{id}
|
|
func (h NetworkScanHandler) DeleteNetworkScanTarget(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodDelete {
|
|
Error(w, http.StatusMethodNotAllowed, "Method not allowed")
|
|
return
|
|
}
|
|
|
|
id := r.PathValue("id")
|
|
if id == "" {
|
|
Error(w, http.StatusBadRequest, "network scan target ID is required")
|
|
return
|
|
}
|
|
|
|
if err := h.svc.DeleteTarget(r.Context(), id); err != nil {
|
|
Error(w, http.StatusNotFound, fmt.Sprintf("failed to delete network scan target: %v", err))
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusNoContent, nil)
|
|
}
|
|
|
|
// TriggerNetworkScan handles POST /api/v1/network-scan-targets/{id}/scan
|
|
func (h NetworkScanHandler) TriggerNetworkScan(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
Error(w, http.StatusMethodNotAllowed, "Method not allowed")
|
|
return
|
|
}
|
|
|
|
id := r.PathValue("id")
|
|
if id == "" {
|
|
Error(w, http.StatusBadRequest, "network scan target ID is required")
|
|
return
|
|
}
|
|
|
|
scan, err := h.svc.TriggerScan(r.Context(), id)
|
|
if err != nil {
|
|
Error(w, http.StatusInternalServerError, fmt.Sprintf("failed to trigger scan: %v", err))
|
|
return
|
|
}
|
|
|
|
// scan may be nil if no certs found
|
|
if scan == nil {
|
|
JSON(w, http.StatusOK, map[string]string{
|
|
"status": "completed",
|
|
"message": "Scan completed, no certificates found",
|
|
})
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusAccepted, scan)
|
|
}
|