using AmtScanner.Api.Data; using AmtScanner.Api.Models; using AmtScanner.Api.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Net.Sockets; namespace AmtScanner.Api.Controllers; [ApiController] [Route("api/[controller]")] public class DevicesController : ControllerBase { private readonly AppDbContext _context; private readonly ILogger _logger; private readonly IAmtPowerService _powerService; private readonly ICredentialService _credentialService; public DevicesController( AppDbContext context, ILogger logger, IAmtPowerService powerService, ICredentialService credentialService) { _context = context; _logger = logger; _powerService = powerService; _credentialService = credentialService; } [HttpGet] public async Task>>> GetAllDevices() { var devices = await _context.AmtDevices.ToListAsync(); return Ok(ApiResponse>.Success(devices)); } [HttpGet("{id}")] public async Task>> GetDevice(long id) { var device = await _context.AmtDevices.FindAsync(id); if (device == null) { return Ok(ApiResponse.Fail(404, "设备不存在")); } return Ok(ApiResponse.Success(device)); } [HttpDelete("{id}")] public async Task>> DeleteDevice(long id) { var device = await _context.AmtDevices.FindAsync(id); if (device == null) { return Ok(ApiResponse.Fail(404, "设备不存在")); } _context.AmtDevices.Remove(device); await _context.SaveChangesAsync(); return Ok(ApiResponse.Success(null, "删除成功")); } /// /// 手动添加设备 /// [HttpPost] public async Task>> AddDevice([FromBody] AddDeviceRequest request) { // 验证 IP 地址格式 if (string.IsNullOrWhiteSpace(request.IpAddress)) { return Ok(ApiResponse.Fail(400, "IP 地址不能为空")); } // 检查设备是否已存在 var existingDevice = await _context.AmtDevices.FirstOrDefaultAsync(d => d.IpAddress == request.IpAddress); if (existingDevice != null) { return Ok(ApiResponse.Fail(400, $"设备 {request.IpAddress} 已存在")); } var device = new AmtDevice { IpAddress = request.IpAddress, Hostname = request.Hostname, Description = request.Description, MajorVersion = 0, MinorVersion = 0, ProvisioningState = ProvisioningState.UNKNOWN, AmtOnline = false, OsOnline = false, DiscoveredAt = DateTime.UtcNow, LastSeenAt = DateTime.UtcNow }; // 如果提供了 Windows 凭据,一并保存 if (!string.IsNullOrEmpty(request.WindowsUsername)) { device.WindowsUsername = request.WindowsUsername; if (!string.IsNullOrEmpty(request.WindowsPassword)) { device.WindowsPassword = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(request.WindowsPassword)); } } _context.AmtDevices.Add(device); await _context.SaveChangesAsync(); _logger.LogInformation("Manually added device {Ip}", request.IpAddress); return Ok(ApiResponse.Success(device, "设备添加成功")); } /// /// 更新设备信息 /// [HttpPut("{id}")] public async Task>> UpdateDevice(long id, [FromBody] UpdateDeviceRequest request) { var device = await _context.AmtDevices.FindAsync(id); if (device == null) { return Ok(ApiResponse.Fail(404, "设备不存在")); } if (!string.IsNullOrEmpty(request.Hostname)) device.Hostname = request.Hostname; if (!string.IsNullOrEmpty(request.Description)) device.Description = request.Description; await _context.SaveChangesAsync(); return Ok(ApiResponse.Success(device, "更新成功")); } /// /// 设置设备的 Windows 登录凭据 /// [HttpPut("{id}/credentials")] public async Task>> SetDeviceCredentials(long id, [FromBody] SetDeviceCredentialsRequest request) { var device = await _context.AmtDevices.FindAsync(id); if (device == null) { return Ok(ApiResponse.Fail(404, "设备不存在")); } device.WindowsUsername = request.Username; // 简单加密存储密码(生产环境应使用更安全的加密方式) device.WindowsPassword = string.IsNullOrEmpty(request.Password) ? null : Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(request.Password)); await _context.SaveChangesAsync(); _logger.LogInformation("Updated Windows credentials for device {Id} ({Ip})", id, device.IpAddress); return Ok(ApiResponse.Success(null, "凭据设置成功")); } /// /// 获取设备的 Windows 凭据(仅返回用户名,不返回密码) /// [HttpGet("{id}/credentials")] public async Task>> GetDeviceCredentials(long id) { var device = await _context.AmtDevices.FindAsync(id); if (device == null) { return Ok(ApiResponse.Fail(404, "设备不存在")); } return Ok(ApiResponse.Success(new DeviceCredentialsDto { DeviceId = device.Id, Username = device.WindowsUsername, HasPassword = !string.IsNullOrEmpty(device.WindowsPassword) })); } /// /// 检测所有设备的在线状态 /// [HttpGet("status")] public async Task>>> CheckAllDevicesStatus() { var devices = await _context.AmtDevices.ToListAsync(); var credentials = await _context.AmtCredentials.ToListAsync(); var statusList = new List(); // 并行检测所有设备 var tasks = devices.Select(async device => { var (amtOnline, openPorts) = await CheckAmtOnlineAsync(device.IpAddress); var osOnline = false; // 如果 AMT 在线,尝试查询电源状态来判断 OS 是否在线 if (amtOnline && openPorts.Count > 0) { osOnline = await CheckOsOnlineAsync(device.IpAddress, openPorts, credentials); } // 更新数据库中的在线状态 device.AmtOnline = amtOnline; device.OsOnline = osOnline; if (amtOnline) { device.LastSeenAt = DateTime.UtcNow; } return new DeviceStatusDto { Id = device.Id, IpAddress = device.IpAddress, AmtOnline = amtOnline, OsOnline = osOnline }; }); statusList = (await Task.WhenAll(tasks)).ToList(); // 保存更新 await _context.SaveChangesAsync(); return Ok(ApiResponse>.Success(statusList)); } /// /// 检测单个设备的在线状态 /// [HttpGet("{id}/status")] public async Task>> CheckDeviceStatus(long id) { var device = await _context.AmtDevices.FindAsync(id); if (device == null) { return Ok(ApiResponse.Fail(404, "设备不存在")); } var credentials = await _context.AmtCredentials.ToListAsync(); var (amtOnline, openPorts) = await CheckAmtOnlineAsync(device.IpAddress); var osOnline = false; // 如果 AMT 在线,尝试查询电源状态来判断 OS 是否在线 if (amtOnline && openPorts.Count > 0) { osOnline = await CheckOsOnlineAsync(device.IpAddress, openPorts, credentials); } // 更新数据库 device.AmtOnline = amtOnline; device.OsOnline = osOnline; if (amtOnline) { device.LastSeenAt = DateTime.UtcNow; } await _context.SaveChangesAsync(); return Ok(ApiResponse.Success(new DeviceStatusDto { Id = device.Id, IpAddress = device.IpAddress, AmtOnline = amtOnline, OsOnline = osOnline })); } /// /// 检测 AMT 是否在线(通过尝试连接AMT端口) /// private async Task<(bool isOnline, List openPorts)> CheckAmtOnlineAsync(string ipAddress) { int[] amtPorts = { 16992, 16993 }; var openPorts = new List(); foreach (var port in amtPorts) { try { using var client = new TcpClient(); var connectTask = client.ConnectAsync(ipAddress, port); // 设置超时时间为2秒(增加超时以应对网络延迟) if (await Task.WhenAny(connectTask, Task.Delay(2000)) == connectTask) { if (client.Connected) { openPorts.Add(port); _logger.LogInformation("Device {Ip} AMT port {Port} is open", ipAddress, port); } } else { _logger.LogInformation("Device {Ip} AMT port {Port} connection timeout", ipAddress, port); } } catch (Exception ex) { _logger.LogInformation("Failed to connect to {Ip}:{Port}: {Error}", ipAddress, port, ex.Message); } } var isOnline = openPorts.Count > 0; _logger.LogInformation("Device {Ip} AMT online: {Online}, open ports: [{Ports}]", ipAddress, isOnline, string.Join(", ", openPorts)); return (isOnline, openPorts); } /// /// 检测操作系统是否在线(通过查询电源状态) /// private async Task CheckOsOnlineAsync(string ipAddress, List openPorts, List credentials) { // 尝试使用所有凭据查询电源状态 foreach (var credential in credentials) { try { var decryptedPassword = _credentialService.DecryptPassword(credential.Password); var powerState = await _powerService.GetPowerStateAsync( ipAddress, credential.Username, decryptedPassword, openPorts); if (powerState.Success) { // PowerState = 2 表示开机(操作系统在运行) var osOnline = powerState.PowerState == 2; _logger.LogInformation("Device {Ip} OS online: {Online} (PowerState: {State} - {StateText})", ipAddress, osOnline, powerState.PowerState, powerState.PowerStateText); return osOnline; } else { _logger.LogWarning("Device {Ip} failed to get power state: {Error}", ipAddress, powerState.Error); } } catch (Exception ex) { _logger.LogWarning("Failed to get power state for {Ip} with credential {User}: {Error}", ipAddress, credential.Username, ex.Message); } } // 如果无法查询电源状态,返回 false _logger.LogWarning("Device {Ip} OS online status unknown (no valid credentials or query failed)", ipAddress); return false; } } /// /// 设备状态DTO /// public class DeviceStatusDto { public long Id { get; set; } public string IpAddress { get; set; } = string.Empty; public bool AmtOnline { get; set; } public bool OsOnline { get; set; } } /// /// 更新设备请求 /// public class UpdateDeviceRequest { public string? Hostname { get; set; } public string? Description { get; set; } } /// /// 设置设备 Windows 凭据请求 /// public class SetDeviceCredentialsRequest { public string? Username { get; set; } public string? Password { get; set; } } /// /// 设备凭据DTO /// public class DeviceCredentialsDto { public long DeviceId { get; set; } public string? Username { get; set; } public bool HasPassword { get; set; } } /// /// 添加设备请求 /// public class AddDeviceRequest { public string IpAddress { get; set; } = string.Empty; public string? Hostname { get; set; } public string? Description { get; set; } public string? WindowsUsername { get; set; } public string? WindowsPassword { get; set; } }