package system import ( "context" "runtime" "strings" "syscall/js" "github.com/netbirdio/netbird/version" ) // UpdateStaticInfoAsync is a no-op on JS as there is no static info to update func UpdateStaticInfoAsync() { // do nothing } // GetInfo retrieves system information for WASM environment func GetInfo(_ context.Context) *Info { info := &Info{ GoOS: runtime.GOOS, Kernel: runtime.GOARCH, KernelVersion: runtime.GOARCH, Platform: runtime.GOARCH, OS: runtime.GOARCH, Hostname: "wasm-client", CPUs: runtime.NumCPU(), NetbirdVersion: version.NetbirdVersion(), } collectBrowserInfo(info) collectLocationInfo(info) collectSystemInfo(info) return info } func collectBrowserInfo(info *Info) { navigator := js.Global().Get("navigator") if navigator.IsUndefined() { return } collectUserAgent(info, navigator) collectPlatform(info, navigator) collectCPUInfo(info, navigator) } func collectUserAgent(info *Info, navigator js.Value) { ua := navigator.Get("userAgent") if ua.IsUndefined() { return } userAgent := ua.String() os, osVersion := parseOSFromUserAgent(userAgent) if os != "" { info.OS = os } if osVersion != "" { info.OSVersion = osVersion } } func collectPlatform(info *Info, navigator js.Value) { // Try regular platform property if plat := navigator.Get("platform"); !plat.IsUndefined() { if platStr := plat.String(); platStr != "" { info.Platform = platStr } } // Try newer userAgentData API for more accurate platform userAgentData := navigator.Get("userAgentData") if userAgentData.IsUndefined() { return } platformInfo := userAgentData.Get("platform") if !platformInfo.IsUndefined() { if platStr := platformInfo.String(); platStr != "" { info.Platform = platStr } } } func collectCPUInfo(info *Info, navigator js.Value) { hardwareConcurrency := navigator.Get("hardwareConcurrency") if !hardwareConcurrency.IsUndefined() { info.CPUs = hardwareConcurrency.Int() } } func collectLocationInfo(info *Info) { location := js.Global().Get("location") if location.IsUndefined() { return } if host := location.Get("hostname"); !host.IsUndefined() { hostnameStr := host.String() if hostnameStr != "" && hostnameStr != "localhost" { info.Hostname = hostnameStr } } } func checkFileAndProcess(_ []string) ([]File, error) { return []File{}, nil } func collectSystemInfo(info *Info) { navigator := js.Global().Get("navigator") if navigator.IsUndefined() { return } if vendor := navigator.Get("vendor"); !vendor.IsUndefined() { info.SystemManufacturer = vendor.String() } if product := navigator.Get("product"); !product.IsUndefined() { info.SystemProductName = product.String() } if userAgent := navigator.Get("userAgent"); !userAgent.IsUndefined() { ua := userAgent.String() info.Environment = detectEnvironmentFromUA(ua) } } func parseOSFromUserAgent(userAgent string) (string, string) { if userAgent == "" { return "", "" } switch { case strings.Contains(userAgent, "Windows NT"): return parseWindowsVersion(userAgent) case strings.Contains(userAgent, "Mac OS X"): return parseMacOSVersion(userAgent) case strings.Contains(userAgent, "FreeBSD"): return "FreeBSD", "" case strings.Contains(userAgent, "OpenBSD"): return "OpenBSD", "" case strings.Contains(userAgent, "NetBSD"): return "NetBSD", "" case strings.Contains(userAgent, "Linux"): return parseLinuxVersion(userAgent) case strings.Contains(userAgent, "iPhone") || strings.Contains(userAgent, "iPad"): return parseiOSVersion(userAgent) case strings.Contains(userAgent, "CrOS"): return "ChromeOS", "" default: return "", "" } } func parseWindowsVersion(userAgent string) (string, string) { switch { case strings.Contains(userAgent, "Windows NT 10.0; Win64; x64"): return "Windows", "10/11" case strings.Contains(userAgent, "Windows NT 10.0"): return "Windows", "10" case strings.Contains(userAgent, "Windows NT 6.3"): return "Windows", "8.1" case strings.Contains(userAgent, "Windows NT 6.2"): return "Windows", "8" case strings.Contains(userAgent, "Windows NT 6.1"): return "Windows", "7" default: return "Windows", "Unknown" } } func parseMacOSVersion(userAgent string) (string, string) { idx := strings.Index(userAgent, "Mac OS X ") if idx == -1 { return "macOS", "Unknown" } versionStart := idx + len("Mac OS X ") versionEnd := strings.Index(userAgent[versionStart:], ")") if versionEnd <= 0 { return "macOS", "Unknown" } ver := userAgent[versionStart : versionStart+versionEnd] ver = strings.ReplaceAll(ver, "_", ".") return "macOS", ver } func parseLinuxVersion(userAgent string) (string, string) { if strings.Contains(userAgent, "Android") { return "Android", extractAndroidVersion(userAgent) } if strings.Contains(userAgent, "Ubuntu") { return "Ubuntu", "" } return "Linux", "" } func parseiOSVersion(userAgent string) (string, string) { idx := strings.Index(userAgent, "OS ") if idx == -1 { return "iOS", "Unknown" } versionStart := idx + 3 versionEnd := strings.Index(userAgent[versionStart:], " ") if versionEnd <= 0 { return "iOS", "Unknown" } ver := userAgent[versionStart : versionStart+versionEnd] ver = strings.ReplaceAll(ver, "_", ".") return "iOS", ver } func extractAndroidVersion(userAgent string) string { if idx := strings.Index(userAgent, "Android "); idx != -1 { versionStart := idx + len("Android ") versionEnd := strings.IndexAny(userAgent[versionStart:], ";)") if versionEnd > 0 { return userAgent[versionStart : versionStart+versionEnd] } } return "Unknown" } func detectEnvironmentFromUA(_ string) Environment { return Environment{} }