update news & events

This commit is contained in:
2025-10-22 09:41:40 +07:00
parent 40cf6fe6f4
commit 8f81545293
348 changed files with 111475 additions and 623 deletions

View File

@ -0,0 +1,95 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
#if WINDOWS
using System.Management; // NuGet: System.Management
#endif
namespace TWASys_App.Models.ServerInfo
{
public record MachineHwIds(string? SerialNumber, string? BiosVendor, string? ProductUuid);
public static class HwIdReader
{
public static MachineHwIds Get()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
#if WINDOWS
string? serial = null, biosVendor = null, uuid = null;
try
{
using var cs = new ManagementObjectSearcher(
"SELECT UUID, IdentifyingNumber FROM Win32_ComputerSystemProduct");
foreach (ManagementObject mo in cs.Get())
{
uuid = mo["UUID"]?.ToString();
serial = mo["IdentifyingNumber"]?.ToString(); // system serial
break;
}
} catch { /* ignore */ }
try
{
using var bios = new ManagementObjectSearcher(
"SELECT Manufacturer FROM Win32_BIOS");
foreach (ManagementObject mo in bios.Get())
{
biosVendor = mo["Manufacturer"]?.ToString();
break;
}
} catch { /* ignore */ }
return new MachineHwIds(Null(serial), Null(biosVendor), Null(uuid));
#else
return new MachineHwIds(null, null, null);
#endif
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
string R(string p) => File.Exists(p) ? File.ReadAllText(p).Trim() : "";
var serial = R("/sys/class/dmi/id/product_serial");
var vendor = R("/sys/class/dmi/id/bios_vendor");
var uuid = R("/sys/class/dmi/id/product_uuid");
// Fallback (nếu sysfs rỗng và bạn cho phép): dmidecode (có thể cần sudo)
if (string.IsNullOrWhiteSpace(serial)) serial = Exec("dmidecode", "-s system-serial-number");
if (string.IsNullOrWhiteSpace(vendor)) vendor = Exec("dmidecode", "-s bios-vendor");
if (string.IsNullOrWhiteSpace(uuid)) uuid = Exec("dmidecode", "-s system-uuid");
return new MachineHwIds(Null(serial), Null(vendor), Null(uuid));
}
// macOS không có “BIOS”; có thể lấy serial máy nhưng BIOS vendor/UUID không tương đương
return new MachineHwIds(null, null, null);
}
static string? Null(string s) => string.IsNullOrWhiteSpace(s) ? null : s;
static string Exec(string file, string args)
{
try
{
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = file,
Arguments = args,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
p.Start();
var s = p.StandardOutput.ReadToEnd().Trim();
p.WaitForExit(2000);
return s;
}
catch { return ""; }
}
}
}

View File

@ -0,0 +1,123 @@
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
namespace TWASys_App.Models.ServerInfo
{
public class NetUtils
{
public static string? GetPrimaryPrivateIPv4String()
{
if (!Socket.OSSupportsIPv4) return null;
try
{
using var s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
s.Connect(new IPEndPoint(IPAddress.Parse("8.8.8.8"), 53));
return (s.LocalEndPoint as IPEndPoint)?.Address.ToString();
}
catch
{
// fallback: liệt kê NIC
return GetAllPrivateIPsStrings().FirstOrDefault(ip => ip.Contains('.'));
}
}
public static string? GetPrimaryPrivateIPv6String()
{
if (!Socket.OSSupportsIPv6) return null;
try
{
using var s = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
s.Connect(new IPEndPoint(IPAddress.Parse("2001:4860:4860::8888"), 53));
var ip = (s.LocalEndPoint as IPEndPoint)?.Address;
return ip is null ? null : FormatIPv6WithScope(ip);
}
catch
{
var ip = FirstIPv6_ByEnum();
return ip is null ? null : FormatIPv6WithScope(ip);
}
}
public static IEnumerable<string> GetAllPrivateIPsStrings()
{
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
{
if (ni.OperationalStatus != OperationalStatus.Up) continue;
if (ni.NetworkInterfaceType is NetworkInterfaceType.Loopback or NetworkInterfaceType.Tunnel) continue;
var idx = ni.GetIPProperties().GetIPv6Properties()?.Index;
foreach (var ua in ni.GetIPProperties().UnicastAddresses)
{
var ip = ua.Address;
if (IPAddress.IsLoopback(ip)) continue;
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
if (IsPrivateIPv4(ip)) yield return ip.ToString();
}
else if (ip.AddressFamily == AddressFamily.InterNetworkV6)
{
if (IsPrivateIPv6(ip)) yield return FormatIPv6WithScope(ip, idx);
}
}
}
}
// ---------- PUBLIC IP (string) ----------
public static async Task<string?> GetPublicIPv4Async(CancellationToken ct = default) =>
await FetchFirstAsync(new[] { "https://api.ipify.org", "https://checkip.amazonaws.com", "https://ipv4.icanhazip.com" }, ct);
public static async Task<string?> GetPublicIPv6Async(CancellationToken ct = default) =>
await FetchFirstAsync(new[] { "https://api6.ipify.org", "https://ipv6.icanhazip.com", "https://ifconfig.co/ip" }, ct);
public static async Task<string?> GetPublicIpAsync(bool preferIPv6 = false, CancellationToken ct = default) =>
preferIPv6 ? await GetPublicIPv6Async(ct) ?? await GetPublicIPv4Async(ct)
: await GetPublicIPv4Async(ct) ?? await GetPublicIPv6Async(ct);
// ---------- helpers ----------
static async Task<string?> FetchFirstAsync(IEnumerable<string> urls, CancellationToken ct)
{
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(3) };
foreach (var u in urls)
try
{
var s = (await http.GetStringAsync(u, ct)).Trim();
if (!string.IsNullOrWhiteSpace(s)) return s;
}
catch { /* thử URL kế */ }
return null;
}
static IPAddress? FirstIPv6_ByEnum() =>
NetworkInterface.GetAllNetworkInterfaces()
.Where(n => n.OperationalStatus == OperationalStatus.Up &&
n.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
n.NetworkInterfaceType != NetworkInterfaceType.Tunnel)
.SelectMany(n => n.GetIPProperties().UnicastAddresses)
.Select(u => u.Address)
.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetworkV6);
static bool IsPrivateIPv4(IPAddress ip)
{
var b = ip.GetAddressBytes();
return b[0] == 10 || (b[0] == 172 && b[1] is >= 16 and <= 31) || (b[0] == 192 && b[1] == 168);
}
static bool IsPrivateIPv6(IPAddress ip)
{
var b = ip.GetAddressBytes();
return ((b[0] & 0xFE) == 0xFC) || (b[0] == 0xFE && (b[1] & 0xC0) == 0x80); // fc00::/7, fe80::/10
}
// KHÔNG đụng IPAddress.ScopeId. Nếu link-local, thêm scope = index của NIC.
static string FormatIPv6WithScope(IPAddress ip, int? nicIndex = null)
{
if (ip.AddressFamily != AddressFamily.InterNetworkV6) return ip.ToString();
var b = ip.GetAddressBytes();
bool linkLocal = b[0] == 0xFE && (b[1] & 0xC0) == 0x80;
if (linkLocal && nicIndex is int idx) return $"{ip}%{idx}";
return ip.ToString();
}
}
}

View File

@ -0,0 +1,128 @@
using Microsoft.Win32;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace TWASys_App.Models.ServerInfo
{
public record OsInfo(string Platform, string Name, string Version, string Kernel, string Architecture);
public class OsInfoReader
{
public static OsInfo Get()
{
var arch = RuntimeInformation.OSArchitecture.ToString();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return GetWindows(arch);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return GetLinux(arch);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return GetMac(arch);
return new OsInfo(RuntimeInformation.OSDescription, "Unknown", "Unknown", "Unknown", arch);
}
// ---------- Windows ----------
static OsInfo GetWindows(string arch)
{
string name = "Windows";
string version = "Unknown";
try
{
using var k = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
var productName = k?.GetValue("ProductName")?.ToString();
var displayVer = k?.GetValue("DisplayVersion")?.ToString() ?? k?.GetValue("ReleaseId")?.ToString();
var build = k?.GetValue("CurrentBuildNumber")?.ToString();
var ubrObj = k?.GetValue("UBR");
var ubr = ubrObj is int i ? i.ToString() : ubrObj?.ToString();
name = productName ?? "Windows";
if (!string.IsNullOrWhiteSpace(build))
version = $"{(displayVer ?? "")}".Trim();
if (!string.IsNullOrWhiteSpace(build))
version = string.IsNullOrWhiteSpace(version) ? $"Build {build}.{ubr}" : $"{version} (Build {build}.{ubr})";
}
catch { /* fallback below */ }
var kernel = RuntimeInformation.OSDescription; // e.g. "Microsoft Windows 10.0.19045"
return new OsInfo("Windows", name, version, kernel, arch);
}
// ---------- Linux ----------
static OsInfo GetLinux(string arch)
{
string name = "Linux";
string version = "Unknown";
try
{
var dict = ParseKeyValueFile("/etc/os-release"); // NAME, PRETTY_NAME, VERSION_ID...
if (dict.TryGetValue("NAME", out var n)) name = TrimQuotes(n);
if (dict.TryGetValue("PRETTY_NAME", out var pretty)) version = TrimQuotes(pretty);
else if (dict.TryGetValue("VERSION", out var v)) version = TrimQuotes(v);
else if (dict.TryGetValue("VERSION_ID", out var vid)) version = TrimQuotes(vid);
}
catch { /* ignore */ }
var kernel = Run("uname", "-sr"); // e.g. "Linux 6.8.0-40-generic"
if (string.IsNullOrWhiteSpace(kernel)) kernel = RuntimeInformation.OSDescription;
return new OsInfo("Linux", name, version, kernel, arch);
}
// ---------- macOS ----------
static OsInfo GetMac(string arch)
{
var name = Run("sw_vers", "-productName"); // "macOS"
var version = Run("sw_vers", "-productVersion"); // "14.6.1"
if (string.IsNullOrWhiteSpace(name)) name = "macOS";
if (string.IsNullOrWhiteSpace(version)) version = "Unknown";
var kernel = Run("uname", "-sr"); // "Darwin 23.6.0"
if (string.IsNullOrWhiteSpace(kernel)) kernel = RuntimeInformation.OSDescription;
return new OsInfo("macOS", name.Trim(), version.Trim(), kernel.Trim(), arch);
}
// ---------- helpers ----------
static Dictionary<string, string> ParseKeyValueFile(string path)
{
var d = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (!File.Exists(path)) return d;
foreach (var raw in File.ReadAllLines(path))
{
var line = raw.Trim();
if (line.Length == 0 || line.StartsWith("#")) continue;
var i = line.IndexOf('=');
if (i <= 0) continue;
d[line[..i]] = line[(i + 1)..];
}
return d;
}
static string TrimQuotes(string s) => s.Trim().Trim('"');
static string Run(string file, string args)
{
try
{
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = file,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
p.Start();
var s = p.StandardOutput.ReadToEnd().Trim();
p.WaitForExit(1500);
return s;
}
catch { return ""; }
}
}
}

View File

@ -0,0 +1,185 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
#if WINDOWS
using System.Management; // NuGet: System.Management
using Microsoft.Win32;
#endif
namespace TWASys_App.Models.ServerInfo
{
public record SysInfo(int SocketCount, string? CpuName, ulong TotalRamBytes);
public static class SysInfoReader
{
public static SysInfo Get()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return GetWindows();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return GetLinux();
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return GetMac();
return new SysInfo(0, null, 0);
}
// ---------- Windows ----------
static SysInfo GetWindows()
{
string? name = null;
int sockets = 0;
#if WINDOWS
// CPU name: registry
try
{
using var k = Registry.LocalMachine.OpenSubKey(
@"HARDWARE\DESCRIPTION\System\CentralProcessor\0");
name = k?.GetValue("ProcessorNameString")?.ToString()?.Trim();
} catch { }
// Sockets: mỗi Win32_Processor ~ 1 physical socket
try
{
using var s = new ManagementObjectSearcher(
"SELECT SocketDesignation FROM Win32_Processor");
foreach (ManagementObject _ in s.Get()) sockets++;
}
catch
{
// Fallback
try
{
using var s2 = new ManagementObjectSearcher(
"SELECT NumberOfProcessors FROM Win32_ComputerSystem");
foreach (ManagementObject mo in s2.Get())
sockets = Convert.ToInt32(mo["NumberOfProcessors"]);
} catch { }
}
#endif
// RAM: GlobalMemoryStatusEx
ulong total = GetTotalRamWindows();
return new SysInfo(sockets, name, total);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct MEMORYSTATUSEX
{
public uint dwLength;
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
static ulong GetTotalRamWindows()
{
var st = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf<MEMORYSTATUSEX>() };
return GlobalMemoryStatusEx(ref st) ? st.ullTotalPhys : 0UL;
}
// ---------- Linux ----------
static SysInfo GetLinux()
{
// CPU name
string? name = null;
try
{
foreach (var line in File.ReadLines("/proc/cpuinfo"))
if (line.StartsWith("model name", StringComparison.OrdinalIgnoreCase))
{ name = line[(line.IndexOf(':') + 1)..].Trim(); break; }
}
catch { }
if (string.IsNullOrWhiteSpace(name))
name = Run("bash", "-lc \"lscpu | grep -i 'Model name' | cut -d: -f2- | xargs\"");
// Socket count
int sockets = 0;
try
{
var set = new HashSet<string>(StringComparer.Ordinal);
foreach (var dir in Directory.GetDirectories("/sys/devices/system/cpu", "cpu*"))
{
var dn = Path.GetFileName(dir);
if (!dn.StartsWith("cpu") || !int.TryParse(dn[3..], out _)) continue;
var p = Path.Combine(dir, "topology", "physical_package_id");
if (File.Exists(p)) { var v = File.ReadAllText(p).Trim(); if (v != "") set.Add(v); }
}
sockets = set.Count;
}
catch { }
if (sockets == 0)
{
var s = Run("bash", "-lc \"lscpu | grep -i 'Socket(s)' | awk -F: '{print $2}' | xargs\"");
int.TryParse(s, out sockets);
}
// Total RAM: /proc/meminfo (MemTotal: kB)
ulong total = 0;
try
{
foreach (var line in File.ReadLines("/proc/meminfo"))
{
if (line.StartsWith("MemTotal:", StringComparison.Ordinal))
{
var parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
// ví dụ: "MemTotal: 16367472 kB"
if (parts.Length >= 2 && ulong.TryParse(parts[1], out var kb))
total = kb * 1024UL;
break;
}
}
}
catch { }
if (total == 0)
{
var s = Run("bash", "-lc \"free -b | awk '/^Mem:/ {print $2}'\"");
ulong.TryParse(s, out total);
}
return new SysInfo(sockets, string.IsNullOrWhiteSpace(name) ? null : name, total);
}
// ---------- macOS ----------
static SysInfo GetMac()
{
var name = Run("sysctl", "-n machdep.cpu.brand_string");
var sSockets = Run("sysctl", "-n hw.packages"); // số package
int.TryParse(sSockets, out var sockets);
var mem = Run("sysctl", "-n hw.memsize");
ulong.TryParse(mem, out var total);
return new SysInfo(sockets, string.IsNullOrWhiteSpace(name) ? null : name.Trim(), total);
}
static string Run(string file, string args)
{
try
{
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = file,
Arguments = args,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
p.Start();
var txt = p.StandardOutput.ReadToEnd();
p.WaitForExit(2000);
return txt.Trim();
}
catch { return ""; }
}
}
}