add achats import and hardware analyse
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user