package handlers import ( "net/http" "regexp" "strings" "github.com/gin-gonic/gin" ) type analyseHardwareRequest struct { Type string `json:"type"` Texte string `json:"texte"` } type lspciDevice struct { Slot string `json:"slot"` Class string `json:"class"` Description string `json:"description"` Subsystem string `json:"subsystem,omitempty"` Driver string `json:"driver,omitempty"` Modules []string `json:"modules,omitempty"` Raw []string `json:"raw,omitempty"` } type lsusbDevice struct { Bus string `json:"bus"` Device string `json:"device"` VendorID string `json:"vendor_id"` ProductID string `json:"product_id"` Description string `json:"description"` Raw string `json:"raw,omitempty"` } type analyseHardwareResponse struct { Type string `json:"type"` Count int `json:"count"` Devices []interface{} `json:"devices"` } // @Summary Analyser une sortie hardware (lspci/lsusb) // @Tags Analyse // @Accept json // @Produce json // @Param body body analyseHardwareRequest true "Texte a analyser" // @Success 200 {object} analyseHardwareResponse // @Failure 400 {object} map[string]string // @Router /analyse-hardware [post] func (h *Handler) AnalyseHardware(c *gin.Context) { var req analyseHardwareRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"erreur": "donnees invalides"}) return } req.Type = strings.ToLower(strings.TrimSpace(req.Type)) req.Texte = strings.TrimSpace(req.Texte) if req.Texte == "" { c.JSON(http.StatusBadRequest, gin.H{"erreur": "texte vide"}) return } if req.Type == "" { req.Type = detectHardwareType(req.Texte) } switch req.Type { case "lspci": devices := parseLspci(req.Texte) c.JSON(http.StatusOK, analyseHardwareResponse{ Type: "lspci", Count: len(devices), Devices: toInterfaceSlice(devices), }) case "lsusb": devices := parseLsusb(req.Texte) c.JSON(http.StatusOK, analyseHardwareResponse{ Type: "lsusb", Count: len(devices), Devices: toInterfaceSlice(devices), }) default: c.JSON(http.StatusBadRequest, gin.H{"erreur": "type non supporte"}) } } func detectHardwareType(text string) string { if strings.Contains(text, "Bus ") && strings.Contains(text, "ID ") { return "lsusb" } return "lspci" } func parseLspci(text string) []lspciDevice { lines := strings.Split(text, "\n") var devices []lspciDevice re := regexp.MustCompile(`^(?:[0-9a-fA-F]{4}:)?([0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F])\s+([^:]+):\s+(.+)$`) var current *lspciDevice for _, raw := range lines { line := strings.TrimRight(raw, "\r") if line == "" { continue } if matches := re.FindStringSubmatch(line); matches != nil { if current != nil { devices = append(devices, *current) } current = &lspciDevice{ Slot: matches[1], Class: strings.TrimSpace(matches[2]), Description: strings.TrimSpace(matches[3]), Raw: []string{line}, } continue } if current == nil { continue } trimmed := strings.TrimSpace(line) current.Raw = append(current.Raw, line) if strings.HasPrefix(trimmed, "Subsystem:") { current.Subsystem = strings.TrimSpace(strings.TrimPrefix(trimmed, "Subsystem:")) } else if strings.HasPrefix(trimmed, "Kernel driver in use:") { current.Driver = strings.TrimSpace(strings.TrimPrefix(trimmed, "Kernel driver in use:")) } else if strings.HasPrefix(trimmed, "Kernel modules:") { value := strings.TrimSpace(strings.TrimPrefix(trimmed, "Kernel modules:")) if value != "" { current.Modules = splitCSV(value) } } } if current != nil { devices = append(devices, *current) } return devices } func parseLsusb(text string) []lsusbDevice { lines := strings.Split(text, "\n") var devices []lsusbDevice re := regexp.MustCompile(`^Bus\s+(\d+)\s+Device\s+(\d+):\s+ID\s+([0-9a-fA-F]{4}):([0-9a-fA-F]{4})\s*(.*)$`) for _, raw := range lines { line := strings.TrimRight(raw, "\r") if line == "" { continue } matches := re.FindStringSubmatch(line) if matches == nil { continue } devices = append(devices, lsusbDevice{ Bus: matches[1], Device: matches[2], VendorID: matches[3], ProductID: matches[4], Description: strings.TrimSpace(matches[5]), Raw: line, }) } return devices } func splitCSV(value string) []string { parts := strings.Split(value, ",") out := make([]string, 0, len(parts)) for _, part := range parts { part = strings.TrimSpace(part) if part != "" { out = append(out, part) } } return out } func toInterfaceSlice[T any](items []T) []interface{} { out := make([]interface{}, len(items)) for i, item := range items { out[i] = item } return out }