249 lines
7.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<DevicesController> _logger;
private readonly IAmtPowerService _powerService;
private readonly ICredentialService _credentialService;
public DevicesController(
AppDbContext context,
ILogger<DevicesController> logger,
IAmtPowerService powerService,
ICredentialService credentialService)
{
_context = context;
_logger = logger;
_powerService = powerService;
_credentialService = credentialService;
}
[HttpGet]
public async Task<ActionResult<List<AmtDevice>>> GetAllDevices()
{
return await _context.AmtDevices.ToListAsync();
}
[HttpGet("{id}")]
public async Task<ActionResult<AmtDevice>> GetDevice(long id)
{
var device = await _context.AmtDevices.FindAsync(id);
if (device == null)
{
return NotFound();
}
return device;
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteDevice(long id)
{
var device = await _context.AmtDevices.FindAsync(id);
if (device == null)
{
return NotFound();
}
_context.AmtDevices.Remove(device);
await _context.SaveChangesAsync();
return NoContent();
}
/// <summary>
/// 检测所有设备的在线状态
/// </summary>
[HttpGet("status")]
public async Task<ActionResult<List<DeviceStatusDto>>> CheckAllDevicesStatus()
{
var devices = await _context.AmtDevices.ToListAsync();
var credentials = await _context.AmtCredentials.ToListAsync();
var statusList = new List<DeviceStatusDto>();
// 并行检测所有设备
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 statusList;
}
/// <summary>
/// 检测单个设备的在线状态
/// </summary>
[HttpGet("{id}/status")]
public async Task<ActionResult<DeviceStatusDto>> CheckDeviceStatus(long id)
{
var device = await _context.AmtDevices.FindAsync(id);
if (device == null)
{
return NotFound();
}
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 new DeviceStatusDto
{
Id = device.Id,
IpAddress = device.IpAddress,
AmtOnline = amtOnline,
OsOnline = osOnline
};
}
/// <summary>
/// 检测 AMT 是否在线通过尝试连接AMT端口
/// </summary>
private async Task<(bool isOnline, List<int> openPorts)> CheckAmtOnlineAsync(string ipAddress)
{
int[] amtPorts = { 16992, 16993 };
var openPorts = new List<int>();
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);
}
/// <summary>
/// 检测操作系统是否在线(通过查询电源状态)
/// </summary>
private async Task<bool> CheckOsOnlineAsync(string ipAddress, List<int> openPorts, List<AmtCredential> 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;
}
}
/// <summary>
/// 设备状态DTO
/// </summary>
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; }
}