using System.Net.Http; using System.Text; using System.Text.Json; using System.Net; using System.Net.Sockets; using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.VisualBasic.Devices; namespace HeartbeatSender; public class heartbeatSender { private readonly HttpClient httpClient = new(); private string token = ""; private string branchName = ""; private string heartbeatDomain = ""; private CancellationTokenSource? cancellationTokenSource; public heartbeatSender() { this.heartbeatDomain = Utils.Env.Get("HeartBeatUrl", ""); // 在建構子中啟動背景心跳任務 _ = Task.Run(() => StartAsync()); } public async Task StartAsync() { cancellationTokenSource = new CancellationTokenSource(); while (!cancellationTokenSource.IsCancellationRequested) { try { if (string.IsNullOrEmpty(token)) { bool loginSuccess = await LoginAndGetTokenAsync(); Console.WriteLine(loginSuccess ? "心跳登入成功" : "心跳登入失敗"); } if (!string.IsNullOrEmpty(token)) { await SendHeartbeatAsync(); } await Task.Delay(TimeSpan.FromMinutes(1), cancellationTokenSource.Token); } catch (Exception ex) { Console.WriteLine($"心跳任務錯誤:{ex.Message}"); await Task.Delay(5000); // 錯誤後延遲再跑 } } } public void Stop() { cancellationTokenSource?.Cancel(); Console.WriteLine("心跳任務已停止"); } public async Task LoginAndGetTokenAsync() { var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/receiveRegister"; var loginPayload = new { email = "MachineKTV@gmail.com", password = "aa147258-" }; var result = await SendPostAsync(url, loginPayload); if (result.ValueKind == JsonValueKind.Object && result.TryGetProperty("data", out JsonElement data)) { token = data.GetProperty("token").GetString()!; branchName = data.GetProperty("branch_name").GetString()!; return true; } return false; } public async Task SendHeartbeatAsync() { var url = $"{heartbeatDomain.TrimEnd('/')}/api/room/heartbeat"; string hostName = Dns.GetHostName(); var heartbeatPayload = new { branch_name = branchName, hostname = "PC" + hostName[^3..], ip = GetLocalIPv4(), cpu = GetCpuUsage(), memory = GetTotalMemoryInMB(), disk = GetDiskTotalSizeInGB() }; await SendPostAsync(url, heartbeatPayload, token); Console.WriteLine($"心跳送出成功: {DateTime.Now:HH:mm:ss}"); } private async Task SendPostAsync(string url, object payload, string? bearerToken = null) { var json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var request = new HttpRequestMessage(HttpMethod.Post, url) { Content = content }; if (!string.IsNullOrWhiteSpace(bearerToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", bearerToken); } var response = await httpClient.SendAsync(request); response.EnsureSuccessStatusCode(); string responseJson = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize(responseJson); } private static string GetLocalIPv4() { foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList) { if (ip.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(ip)) return ip.ToString(); } return "127.0.0.1"; } private float GetCpuUsage() { using var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); cpuCounter.NextValue(); Thread.Sleep(100); return cpuCounter.NextValue(); } private float GetTotalMemoryInMB() { var ci = new ComputerInfo(); return (ci.TotalPhysicalMemory - ci.AvailablePhysicalMemory) / 1024f; } private float GetDiskTotalSizeInGB(string driveLetter = "C") { var drive = new DriveInfo(driveLetter); return drive.TotalSize / (1024f * 1024f * 1024f); } }