package utils import ( "fmt" "io" "net/http" "os" "os/exec" "runtime" "strings" "github.com/jaypipes/ghw" "golang.org/x/sys/windows/registry" ) type MachineInfo struct { Hostname string `json:"Hostname"` OS string `json:"OS"` Version string `json:"Version"` HWID string `json:"HWID"` ExternalIP string `json:"ExternalIP"` CPU ghw.CPUInfo `json:"CPU"` RAM ghw.MemoryInfo `json:"RAM"` GPU ghw.GPUInfo `json:"GPU"` } func GetMachineInfo() (*MachineInfo, error) { info := &MachineInfo{} // 1. Get Hostname hostname, err := os.Hostname() if err != nil { return nil, fmt.Errorf("failed to get hostname: %w", err) } info.Hostname = hostname // 2. Get OS Info info.OS = fmt.Sprintf("%s %s", runtime.GOOS, runtime.GOARCH) // 3. Get Version Info k, _ := registry.OpenKey( registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE, ) defer k.Close() product, _, _ := k.GetStringValue("ProductName") build, _, _ := k.GetStringValue("CurrentBuild") ubr, _, _ := k.GetIntegerValue("UBR") display, _, _ := k.GetStringValue("DisplayVersion") edition, _, _ := k.GetStringValue("EditionID") // Append edition if available if edition != "" { product = fmt.Sprintf("%s %s", product, edition) } // Split display versione via H (like 23H2, 24H2, 25H2), if its => 23, then its Windows 11, not 10 if strings.HasPrefix(display, "2") { parts := strings.SplitN(display, "H", 2) if len(parts) > 0 { yearPart := parts[0] if yearPartInt := strings.TrimSpace(yearPart); yearPartInt >= "23" { product = "Windows 11" } } } info.Version = fmt.Sprintf("%s %s %s (Build %s.%d)", product, display, edition, build, ubr) // 3. Get HWID (Windows specific via wmic) // Fallback or different implementation needed for Linux/Mac if required if runtime.GOOS == "windows" { out, err := exec.Command("wmic", "csproduct", "get", "uuid").Output() if err == nil { // Parse output which looks like "UUID \n \n\n" lines := strings.Split(string(out), "\n") for _, line := range lines { trimmed := strings.TrimSpace(line) if trimmed != "" && trimmed != "UUID" { info.HWID = trimmed break } } } // Fallback to registry MachineGuid if wmic fails or empty if info.HWID == "" { // Simplified registry read attempt using reg query command to avoid cgo/syscall complexity for now // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography -> MachineGuid out, err := exec.Command("reg", "query", `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`, "/v", "MachineGuid").Output() if err == nil { // Parse output content := string(out) if idx := strings.Index(content, "REG_SZ"); idx != -1 { info.HWID = strings.TrimSpace(content[idx+6:]) } } } } else { info.HWID = "Not implemented for " + runtime.GOOS } // 4. Get External IP ip, err := getExternalIP() if err == nil { info.ExternalIP = ip } else { info.ExternalIP = "Unavailable" } // 5. Get CPU Info cpuInfo, err := getCPUInfo() if err == nil { info.CPU = *cpuInfo } // 6. Get GPU Info gpuInfo, err := getGPUInfo() if err == nil { info.GPU = *gpuInfo } // 7. Get RAM Info ramInfo, err := getRAMInfo() if err == nil { info.RAM = *ramInfo } return info, nil } func getExternalIP() (string, error) { resp, err := http.Get("https://api.ipify.org?format=text") if err != nil { return "", err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "", err } return string(body), nil } func getCPUInfo() (*ghw.CPUInfo, error) { cpuInfo, _ := ghw.CPU() if cpuInfo == nil { return nil, fmt.Errorf("failed to get CPU info") } return cpuInfo, nil } func getGPUInfo() (*ghw.GPUInfo, error) { gpuInfo, err := ghw.GPU() if gpuInfo == nil { return nil, fmt.Errorf("failed to get GPU info: %w", err) } return gpuInfo, nil } func getRAMInfo() (*ghw.MemoryInfo, error) { memory, err := ghw.Memory() if memory == nil { return nil, fmt.Errorf("failed to get RAM info: %w", err) } return memory, nil }