47 changed files with 1543 additions and 1157 deletions
@ -1,33 +0,0 @@ |
|||||
using CoreAgent.Domain.Interfaces; |
|
||||
using MediatR; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
|
|
||||
namespace CoreAgent.Application.Commands.CellularNetwork; |
|
||||
|
|
||||
public class StartCellularNetworkCommandHandler : IRequestHandler<StartCellularNetworkCommand, bool> |
|
||||
{ |
|
||||
private readonly ICellularNetworkService _cellularNetworkService; |
|
||||
private readonly ILogger<StartCellularNetworkCommandHandler> _logger; |
|
||||
|
|
||||
public StartCellularNetworkCommandHandler( |
|
||||
ICellularNetworkService cellularNetworkService, |
|
||||
ILogger<StartCellularNetworkCommandHandler> logger) |
|
||||
{ |
|
||||
_cellularNetworkService = cellularNetworkService; |
|
||||
_logger = logger; |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> Handle(StartCellularNetworkCommand request, CancellationToken cancellationToken) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogInformation("Starting cellular network for interface: {InterfaceName}", request.InterfaceName); |
|
||||
return await _cellularNetworkService.StartAsync(request.InterfaceName, request.Config); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "Error starting cellular network for interface: {InterfaceName}", request.InterfaceName); |
|
||||
throw; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,31 +0,0 @@ |
|||||
using CoreAgent.Domain.Models; |
|
||||
|
|
||||
namespace CoreAgent.Domain.Interfaces; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 蜂窝网络服务接口
|
|
||||
/// </summary>
|
|
||||
public interface ICellularNetworkService |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 启动蜂窝网络
|
|
||||
/// </summary>
|
|
||||
/// <param name="interfaceName">网络接口名称</param>
|
|
||||
/// <param name="config">网络配置</param>
|
|
||||
/// <returns>操作是否成功</returns>
|
|
||||
Task<bool> StartAsync(string interfaceName, CellularNetworkConfig config); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 停止蜂窝网络
|
|
||||
/// </summary>
|
|
||||
/// <param name="interfaceName">网络接口名称</param>
|
|
||||
/// <returns>操作是否成功</returns>
|
|
||||
Task<bool> StopAsync(string interfaceName); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 获取蜂窝网络状态
|
|
||||
/// </summary>
|
|
||||
/// <param name="interfaceName">网络接口名称</param>
|
|
||||
/// <returns>网络状态信息</returns>
|
|
||||
Task<CellularNetworkStatus> GetStatusAsync(string interfaceName); |
|
||||
} |
|
@ -1,28 +0,0 @@ |
|||||
namespace CoreAgent.Domain.Interfaces; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 命令策略工厂接口
|
|
||||
/// </summary>
|
|
||||
public interface ICommandStrategyFactory |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 创建命令策略
|
|
||||
/// </summary>
|
|
||||
/// <returns>命令策略实例</returns>
|
|
||||
ICommandStrategy CreateStrategy(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 命令策略接口
|
|
||||
/// </summary>
|
|
||||
public interface ICommandStrategy |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 执行命令并返回缓冲结果
|
|
||||
/// </summary>
|
|
||||
/// <param name="command">要执行的命令</param>
|
|
||||
/// <param name="source">取消令牌源</param>
|
|
||||
/// <param name="logger">日志记录器</param>
|
|
||||
/// <returns>命令执行结果</returns>
|
|
||||
Task<string> ExecuteBufferedAsync(string command, CancellationTokenSource source); |
|
||||
} |
|
@ -0,0 +1,178 @@ |
|||||
|
using CoreAgent.Domain.Models.Network; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Interfaces.Network; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 蜂窝网络服务接口
|
||||
|
/// </summary>
|
||||
|
public interface ICellularNetworkService |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 启动蜂窝网络
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceName">网络接口名称</param>
|
||||
|
/// <param name="config">网络配置</param>
|
||||
|
/// <returns>启动结果</returns>
|
||||
|
Task<bool> StartAsync(string interfaceName, CellularNetworkConfig config); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 停止蜂窝网络
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceName">网络接口名称</param>
|
||||
|
/// <returns>停止结果</returns>
|
||||
|
Task<bool> StopAsync(string interfaceName); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取网络状态
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceName">网络接口名称</param>
|
||||
|
/// <returns>网络状态信息</returns>
|
||||
|
Task<NetworkStatus> GetNetworkStatusAsync(string interfaceName); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取信号强度
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceName">网络接口名称</param>
|
||||
|
/// <returns>信号强度信息</returns>
|
||||
|
Task<SignalStrength> GetSignalStrengthAsync(string interfaceName); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取网络类型
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceName">网络接口名称</param>
|
||||
|
/// <returns>网络类型信息</returns>
|
||||
|
Task<NetworkType> GetNetworkTypeAsync(string interfaceName); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 设置发射功率
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceName">网络接口名称</param>
|
||||
|
/// <param name="powerLevel">功率等级(0-100)</param>
|
||||
|
/// <returns>设置结果</returns>
|
||||
|
Task<bool> SetTransmitPowerAsync(string interfaceName, int powerLevel); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取全局状态
|
||||
|
/// </summary>
|
||||
|
/// <returns>全局状态信息</returns>
|
||||
|
Task<CellularNetworkGlobalStatus> GetGlobalStatusAsync(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 网络状态
|
||||
|
/// </summary>
|
||||
|
public enum NetworkStatus |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 未知
|
||||
|
/// </summary>
|
||||
|
Unknown, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 已连接
|
||||
|
/// </summary>
|
||||
|
Connected, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 未连接
|
||||
|
/// </summary>
|
||||
|
Disconnected |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 信号强度
|
||||
|
/// </summary>
|
||||
|
public enum SignalStrength |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 无信号
|
||||
|
/// </summary>
|
||||
|
NoSignal, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 弱信号
|
||||
|
/// </summary>
|
||||
|
Weak, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 中等信号
|
||||
|
/// </summary>
|
||||
|
Medium, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 强信号
|
||||
|
/// </summary>
|
||||
|
Strong |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 网络类型
|
||||
|
/// </summary>
|
||||
|
public enum NetworkType |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 未知
|
||||
|
/// </summary>
|
||||
|
Unknown, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 2G网络
|
||||
|
/// </summary>
|
||||
|
G2, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 3G网络
|
||||
|
/// </summary>
|
||||
|
G3, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 4G网络
|
||||
|
/// </summary>
|
||||
|
G4, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 5G网络
|
||||
|
/// </summary>
|
||||
|
G5 |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 蜂窝网络全局状态
|
||||
|
/// </summary>
|
||||
|
public class CellularNetworkGlobalStatus |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 是否已初始化
|
||||
|
/// </summary>
|
||||
|
public bool IsInitialized { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 最后启动时间
|
||||
|
/// </summary>
|
||||
|
public DateTime? LastStartTime { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 最后停止时间
|
||||
|
/// </summary>
|
||||
|
public DateTime? LastStopTime { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 当前运行状态
|
||||
|
/// </summary>
|
||||
|
public NetworkStatus CurrentStatus { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 当前信号强度
|
||||
|
/// </summary>
|
||||
|
public SignalStrength CurrentSignalStrength { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 当前网络类型
|
||||
|
/// </summary>
|
||||
|
public NetworkType CurrentNetworkType { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 当前发射功率
|
||||
|
/// </summary>
|
||||
|
public int CurrentTransmitPower { get; set; } |
||||
|
} |
@ -0,0 +1,127 @@ |
|||||
|
namespace CoreAgent.Domain.Interfaces.Network; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 网络管理器接口
|
||||
|
/// </summary>
|
||||
|
public interface INetworkManager |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 获取所有网络接口信息
|
||||
|
/// </summary>
|
||||
|
/// <returns>网络接口信息列表</returns>
|
||||
|
Task<IEnumerable<NetworkInterfaceInfo>> GetNetworkInterfacesAsync(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 获取指定网络接口的详细信息
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceId">网络接口ID</param>
|
||||
|
/// <returns>网络接口详细信息</returns>
|
||||
|
Task<NetworkInterfaceDetail> GetNetworkInterfaceDetailAsync(string interfaceId); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 启用网络接口
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceId">网络接口ID</param>
|
||||
|
/// <returns>操作是否成功</returns>
|
||||
|
Task<bool> EnableNetworkInterfaceAsync(string interfaceId); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 禁用网络接口
|
||||
|
/// </summary>
|
||||
|
/// <param name="interfaceId">网络接口ID</param>
|
||||
|
/// <returns>操作是否成功</returns>
|
||||
|
Task<bool> DisableNetworkInterfaceAsync(string interfaceId); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 网络接口信息
|
||||
|
/// </summary>
|
||||
|
public class NetworkInterfaceInfo |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 接口ID
|
||||
|
/// </summary>
|
||||
|
public string Id { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 接口名称
|
||||
|
/// </summary>
|
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 接口类型
|
||||
|
/// </summary>
|
||||
|
public NetworkInterfaceType Type { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 是否启用
|
||||
|
/// </summary>
|
||||
|
public bool IsEnabled { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// MAC地址
|
||||
|
/// </summary>
|
||||
|
public string MacAddress { get; set; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 网络接口详细信息
|
||||
|
/// </summary>
|
||||
|
public class NetworkInterfaceDetail : NetworkInterfaceInfo |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// IP地址列表
|
||||
|
/// </summary>
|
||||
|
public IEnumerable<string> IpAddresses { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 子网掩码
|
||||
|
/// </summary>
|
||||
|
public string SubnetMask { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 默认网关
|
||||
|
/// </summary>
|
||||
|
public string DefaultGateway { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// DNS服务器列表
|
||||
|
/// </summary>
|
||||
|
public IEnumerable<string> DnsServers { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 连接速度(Mbps)
|
||||
|
/// </summary>
|
||||
|
public int Speed { get; set; } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 网络接口类型
|
||||
|
/// </summary>
|
||||
|
public enum NetworkInterfaceType |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 未知
|
||||
|
/// </summary>
|
||||
|
Unknown, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 以太网
|
||||
|
/// </summary>
|
||||
|
Ethernet, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 无线网络
|
||||
|
/// </summary>
|
||||
|
Wireless, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 蜂窝网络
|
||||
|
/// </summary>
|
||||
|
Cellular, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 虚拟网络
|
||||
|
/// </summary>
|
||||
|
Virtual |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
using CoreAgent.Domain.Models.System; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Interfaces.System.Command; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 系统命令执行器接口
|
||||
|
/// </summary>
|
||||
|
public interface ISystemCommandExecutor |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 执行命令并实时输出结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="command">要执行的命令</param>
|
||||
|
/// <param name="outputHandler">输出处理器</param>
|
||||
|
/// <param name="cancellationTokenSource">取消令牌源</param>
|
||||
|
/// <returns>执行任务</returns>
|
||||
|
Task ExecuteCommandWithOutputAsync(string command, Action<string> outputHandler, CancellationTokenSource cancellationTokenSource); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行命令并返回结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="command">要执行的命令</param>
|
||||
|
/// <param name="cancellationTokenSource">取消令牌源</param>
|
||||
|
/// <returns>命令执行结果</returns>
|
||||
|
Task<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource); |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
using CoreAgent.Domain.Models; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Interfaces.System.Command; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 系统命令执行器工厂接口
|
||||
|
/// </summary>
|
||||
|
public interface ISystemCommandExecutorFactory |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 创建适合当前操作系统的命令执行器
|
||||
|
/// </summary>
|
||||
|
/// <returns>命令执行器实例</returns>
|
||||
|
ISystemCommandExecutor CreateExecutor(); |
||||
|
} |
@ -1,49 +0,0 @@ |
|||||
using Microsoft.AspNetCore.Mvc; |
|
||||
using System.Net; |
|
||||
|
|
||||
namespace CoreAgent.Domain.Models |
|
||||
{ |
|
||||
public class ApiActionResult : IActionResult |
|
||||
{ |
|
||||
private readonly ApiResponse _response; |
|
||||
private readonly HttpStatusCode _statusCode; |
|
||||
|
|
||||
public ApiActionResult(ApiResponse response, HttpStatusCode statusCode = HttpStatusCode.OK) |
|
||||
{ |
|
||||
_response = response; |
|
||||
_statusCode = statusCode; |
|
||||
} |
|
||||
|
|
||||
public async Task ExecuteResultAsync(ActionContext context) |
|
||||
{ |
|
||||
var objectResult = new ObjectResult(_response) |
|
||||
{ |
|
||||
StatusCode = (int)_statusCode |
|
||||
}; |
|
||||
|
|
||||
await objectResult.ExecuteResultAsync(context); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class ApiActionResult<T> : IActionResult |
|
||||
{ |
|
||||
private readonly ApiResponse<T> _response; |
|
||||
private readonly HttpStatusCode _statusCode; |
|
||||
|
|
||||
public ApiActionResult(ApiResponse<T> response, HttpStatusCode statusCode = HttpStatusCode.OK) |
|
||||
{ |
|
||||
_response = response; |
|
||||
_statusCode = statusCode; |
|
||||
} |
|
||||
|
|
||||
public async Task ExecuteResultAsync(ActionContext context) |
|
||||
{ |
|
||||
var objectResult = new ObjectResult(_response) |
|
||||
{ |
|
||||
StatusCode = (int)_statusCode |
|
||||
}; |
|
||||
|
|
||||
await objectResult.ExecuteResultAsync(context); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,58 +0,0 @@ |
|||||
namespace CoreAgent.Domain.Models |
|
||||
{ |
|
||||
public class ApiResponse<T> |
|
||||
{ |
|
||||
public bool Success { get; set; } |
|
||||
public string? Message { get; set; } |
|
||||
public T? Data { get; set; } |
|
||||
public List<string> Errors { get; set; } |
|
||||
|
|
||||
public ApiResponse() |
|
||||
{ |
|
||||
Success = true; |
|
||||
Errors = new List<string>(); |
|
||||
} |
|
||||
|
|
||||
public static ApiResponse<T> CreateSuccess(T data, string? message = null) |
|
||||
{ |
|
||||
return new ApiResponse<T> |
|
||||
{ |
|
||||
Success = true, |
|
||||
Data = data, |
|
||||
Message = message |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
public static ApiResponse<T> CreateError(string message, List<string>? errors = null) |
|
||||
{ |
|
||||
return new ApiResponse<T> |
|
||||
{ |
|
||||
Success = false, |
|
||||
Message = message, |
|
||||
Errors = errors ?? new List<string>() |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class ApiResponse : ApiResponse<object> |
|
||||
{ |
|
||||
public new static ApiResponse CreateSuccess(string? message = null) |
|
||||
{ |
|
||||
return new ApiResponse |
|
||||
{ |
|
||||
Success = true, |
|
||||
Message = message |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
public new static ApiResponse CreateError(string message, List<string>? errors = null) |
|
||||
{ |
|
||||
return new ApiResponse |
|
||||
{ |
|
||||
Success = false, |
|
||||
Message = message, |
|
||||
Errors = errors ?? new List<string>() |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,119 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.Net; |
||||
|
|
||||
|
namespace CoreAgent.Domain.Models.Common; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// API操作结果
|
||||
|
/// </summary>
|
||||
|
public class ApiActionResult : IActionResult |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 是否成功
|
||||
|
/// </summary>
|
||||
|
public bool IsSuccess { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 消息
|
||||
|
/// </summary>
|
||||
|
public string Message { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 错误代码
|
||||
|
/// </summary>
|
||||
|
public string ErrorCode { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// HTTP状态码
|
||||
|
/// </summary>
|
||||
|
public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建成功结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">消息</param>
|
||||
|
/// <returns>API操作结果</returns>
|
||||
|
public static ApiActionResult Ok(string message = "操作成功") |
||||
|
{ |
||||
|
return new ApiActionResult |
||||
|
{ |
||||
|
IsSuccess = true, |
||||
|
Message = message, |
||||
|
ErrorCode = string.Empty, |
||||
|
StatusCode = HttpStatusCode.OK |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建失败结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">错误消息</param>
|
||||
|
/// <param name="errorCode">错误代码</param>
|
||||
|
/// <param name="statusCode">HTTP状态码</param>
|
||||
|
/// <returns>API操作结果</returns>
|
||||
|
public static ApiActionResult Error(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest) |
||||
|
{ |
||||
|
return new ApiActionResult |
||||
|
{ |
||||
|
IsSuccess = false, |
||||
|
Message = message, |
||||
|
ErrorCode = errorCode, |
||||
|
StatusCode = statusCode |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public Task ExecuteResultAsync(ActionContext context) |
||||
|
{ |
||||
|
var result = new ObjectResult(this) { StatusCode = (int)StatusCode }; |
||||
|
return result.ExecuteResultAsync(context); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 带数据的API操作结果
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">数据类型</typeparam>
|
||||
|
public class ApiActionResult<T> : ApiActionResult |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 数据
|
||||
|
/// </summary>
|
||||
|
public T Data { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建成功结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="data">数据</param>
|
||||
|
/// <param name="message">消息</param>
|
||||
|
/// <returns>API操作结果</returns>
|
||||
|
public static ApiActionResult<T> Ok(T data, string message = "操作成功") |
||||
|
{ |
||||
|
return new ApiActionResult<T> |
||||
|
{ |
||||
|
IsSuccess = true, |
||||
|
Message = message, |
||||
|
ErrorCode = string.Empty, |
||||
|
StatusCode = HttpStatusCode.OK, |
||||
|
Data = data |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建失败结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="message">错误消息</param>
|
||||
|
/// <param name="errorCode">错误代码</param>
|
||||
|
/// <param name="statusCode">HTTP状态码</param>
|
||||
|
/// <returns>API操作结果</returns>
|
||||
|
public static ApiActionResult<T> Error(string message, string errorCode = "ERROR", HttpStatusCode statusCode = HttpStatusCode.BadRequest) |
||||
|
{ |
||||
|
return new ApiActionResult<T> |
||||
|
{ |
||||
|
IsSuccess = false, |
||||
|
Message = message, |
||||
|
ErrorCode = errorCode, |
||||
|
StatusCode = statusCode, |
||||
|
Data = default |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
namespace CoreAgent.Domain.Models.Common; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 错误响应
|
||||
|
/// </summary>
|
||||
|
public class ErrorResponse |
||||
|
{ |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 请求的唯一跟踪标识符
|
||||
|
/// </summary>
|
||||
|
public string TraceId { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 错误消息
|
||||
|
/// </summary>
|
||||
|
public string Message { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 验证错误集合,键为字段名,值为错误消息数组
|
||||
|
/// </summary>
|
||||
|
public IDictionary<string, string[]> Errors { get; set; } |
||||
|
} |
@ -1,23 +0,0 @@ |
|||||
namespace CoreAgent.Domain.Models |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 表示API错误响应的领域模型
|
|
||||
/// </summary>
|
|
||||
public class ErrorResponse |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 请求的唯一跟踪标识符
|
|
||||
/// </summary>
|
|
||||
public string TraceId { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 错误消息
|
|
||||
/// </summary>
|
|
||||
public string Message { get; set; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 验证错误集合,键为字段名,值为错误消息数组
|
|
||||
/// </summary>
|
|
||||
public IDictionary<string, string[]> Errors { get; set; } |
|
||||
} |
|
||||
} |
|
@ -1,4 +1,4 @@ |
|||||
namespace CoreAgent.Domain.Models; |
namespace CoreAgent.Domain.Models.Network; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 蜂窝网络配置
|
/// 蜂窝网络配置
|
@ -1,4 +1,4 @@ |
|||||
namespace CoreAgent.Domain.Models; |
namespace CoreAgent.Domain.Models.Network; |
||||
|
|
||||
/// <summary>
|
/// <summary>
|
||||
/// 蜂窝网络状态
|
/// 蜂窝网络状态
|
@ -0,0 +1,61 @@ |
|||||
|
namespace CoreAgent.Domain.Models.System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 命令执行结果
|
||||
|
/// </summary>
|
||||
|
public class CommandExecutionResult |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 是否成功
|
||||
|
/// </summary>
|
||||
|
public bool IsSuccess { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 输出信息
|
||||
|
/// </summary>
|
||||
|
public string Output { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 错误信息
|
||||
|
/// </summary>
|
||||
|
public string Error { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行时间(毫秒)
|
||||
|
/// </summary>
|
||||
|
public long ExecutionTime { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建成功结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="output">输出信息</param>
|
||||
|
/// <param name="executionTime">执行时间</param>
|
||||
|
/// <returns>命令执行结果</returns>
|
||||
|
public static CommandExecutionResult Success(string output, long executionTime) |
||||
|
{ |
||||
|
return new CommandExecutionResult |
||||
|
{ |
||||
|
IsSuccess = true, |
||||
|
Output = output, |
||||
|
Error = string.Empty, |
||||
|
ExecutionTime = executionTime |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 创建失败结果
|
||||
|
/// </summary>
|
||||
|
/// <param name="error">错误信息</param>
|
||||
|
/// <param name="executionTime">执行时间</param>
|
||||
|
/// <returns>命令执行结果</returns>
|
||||
|
public static CommandExecutionResult Failure(string error, long executionTime) |
||||
|
{ |
||||
|
return new CommandExecutionResult |
||||
|
{ |
||||
|
IsSuccess = false, |
||||
|
Output = string.Empty, |
||||
|
Error = error, |
||||
|
ExecutionTime = executionTime |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -0,0 +1,57 @@ |
|||||
|
using CliWrap; |
||||
|
using CliWrap.Buffered; |
||||
|
using CoreAgent.Domain.Interfaces.System.Command; |
||||
|
using CoreAgent.Domain.Models; |
||||
|
using CoreAgent.Domain.Models.System; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Command.Base; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 命令执行器基类
|
||||
|
/// </summary>
|
||||
|
public abstract class BaseCommandExecutor : ISystemCommandExecutor |
||||
|
{ |
||||
|
protected readonly ILogger _logger; |
||||
|
protected abstract string DefaultShell { get; } |
||||
|
|
||||
|
protected BaseCommandExecutor(ILogger logger) |
||||
|
{ |
||||
|
_logger = logger; |
||||
|
} |
||||
|
|
||||
|
public abstract Task ExecuteCommandWithOutputAsync(string command, Action<string> outputHandler, CancellationTokenSource cancellationTokenSource); |
||||
|
|
||||
|
public abstract Task<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource); |
||||
|
|
||||
|
protected async Task<CommandExecutionResult> ExecuteCommandInternalAsync(string command, string arguments, CancellationTokenSource cancellationTokenSource) |
||||
|
{ |
||||
|
var startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("开始执行命令: {Command}", command); |
||||
|
var cmd = Cli.Wrap(DefaultShell) |
||||
|
.WithArguments(arguments) |
||||
|
.WithValidation(CommandResultValidation.None); |
||||
|
var result = await cmd.ExecuteBufferedAsync(cancellationTokenSource.Token); |
||||
|
var executionTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTime; |
||||
|
|
||||
|
if (result.IsSuccess) |
||||
|
{ |
||||
|
_logger.LogDebug("命令执行成功 - 输出: {Output}", result.StandardOutput); |
||||
|
return CommandExecutionResult.Success(result.StandardOutput, executionTime); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_logger.LogDebug("命令执行失败 - 错误: {Error}", result.StandardError); |
||||
|
return CommandExecutionResult.Failure(result.StandardError, executionTime); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
var executionTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - startTime; |
||||
|
_logger.LogError(ex, "命令执行失败: {Command}", command); |
||||
|
return CommandExecutionResult.Failure(ex.Message, executionTime); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,44 +0,0 @@ |
|||||
using CoreAgent.Domain.Interfaces; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Command; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 命令策略工厂实现
|
|
||||
/// </summary>
|
|
||||
public class CommandStrategyFactory : ICommandStrategyFactory |
|
||||
{ |
|
||||
private readonly ILogger<CommandStrategyFactory> _logger; |
|
||||
private readonly ILoggerFactory _loggerFactory; |
|
||||
|
|
||||
public CommandStrategyFactory(ILogger<CommandStrategyFactory> logger, ILoggerFactory loggerFactory) |
|
||||
{ |
|
||||
_logger = logger; |
|
||||
_loggerFactory = loggerFactory; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 创建适合当前操作系统的命令策略
|
|
||||
/// </summary>
|
|
||||
/// <returns>命令策略实例</returns>
|
|
||||
/// <exception cref="PlatformNotSupportedException">当操作系统不是Windows或Linux时抛出</exception>
|
|
||||
public ICommandStrategy CreateStrategy() |
|
||||
{ |
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|
||||
{ |
|
||||
_logger.LogInformation("Creating Windows command strategy"); |
|
||||
return new WindowsCommandStrategy(_loggerFactory.CreateLogger<WindowsCommandStrategy>()); |
|
||||
} |
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) |
|
||||
{ |
|
||||
_logger.LogInformation("Creating Linux command strategy"); |
|
||||
return new LinuxCommandStrategy(_loggerFactory.CreateLogger<LinuxCommandStrategy>()); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
_logger.LogError("Unsupported operating system"); |
|
||||
throw new PlatformNotSupportedException("Only Windows and Linux are supported."); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,46 @@ |
|||||
|
using CliWrap; |
||||
|
using CliWrap.Buffered; |
||||
|
using CoreAgent.Domain.Interfaces.System.Command; |
||||
|
using CoreAgent.Domain.Models.System; |
||||
|
using CoreAgent.Infrastructure.Command.Base; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Command.Executors; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Linux系统命令执行器实现
|
||||
|
/// </summary>
|
||||
|
public class LinuxCommandExecutor : BaseCommandExecutor |
||||
|
{ |
||||
|
private const string ShellPath = "/bin/bash"; |
||||
|
|
||||
|
public LinuxCommandExecutor(ILogger<LinuxCommandExecutor> logger) : base(logger) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override string DefaultShell => ShellPath; |
||||
|
|
||||
|
public override async Task ExecuteCommandWithOutputAsync(string command, Action<string> outputHandler, CancellationTokenSource cancellationTokenSource) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("开始执行命令: {Command}", command); |
||||
|
var cmd = Cli.Wrap(DefaultShell) |
||||
|
.WithArguments($"-c \"{command}\"") |
||||
|
.WithStandardOutputPipe(PipeTarget.ToDelegate(outputHandler)) |
||||
|
.WithStandardErrorPipe(PipeTarget.ToDelegate(outputHandler)) |
||||
|
.WithValidation(CommandResultValidation.None); |
||||
|
var result = await cmd.ExecuteBufferedAsync(cancellationTokenSource.Token); |
||||
|
_logger.LogInformation("命令执行结果: {IsSuccess}", result.IsSuccess); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "命令执行失败: {Command}", command); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override async Task<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource) |
||||
|
{ |
||||
|
return await ExecuteCommandInternalAsync(command, $"-c \"{command}\"", cancellationTokenSource); |
||||
|
} |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
using CliWrap; |
||||
|
using CliWrap.Buffered; |
||||
|
using CoreAgent.Domain.Interfaces.System.Command; |
||||
|
using CoreAgent.Domain.Models.System; |
||||
|
using CoreAgent.Infrastructure.Command.Base; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Command.Executors; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Windows系统命令执行器实现
|
||||
|
/// </summary>
|
||||
|
public class WindowsCommandExecutor : BaseCommandExecutor |
||||
|
{ |
||||
|
private const string ShellPath = "cmd.exe"; |
||||
|
private const string PowerShellPath = "powershell.exe"; |
||||
|
|
||||
|
public WindowsCommandExecutor(ILogger<WindowsCommandExecutor> logger) : base(logger) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override string DefaultShell => ShellPath; |
||||
|
|
||||
|
public override async Task ExecuteCommandWithOutputAsync(string command, Action<string> outputHandler, CancellationTokenSource cancellationTokenSource) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_logger.LogDebug("开始执行命令: {Command}", command); |
||||
|
var cmd = Cli.Wrap(DefaultShell) |
||||
|
.WithArguments($"/c {command}") |
||||
|
.WithStandardOutputPipe(PipeTarget.ToDelegate(outputHandler)) |
||||
|
.WithStandardErrorPipe(PipeTarget.ToDelegate(outputHandler)) |
||||
|
.WithValidation(CommandResultValidation.None); |
||||
|
var result = await cmd.ExecuteBufferedAsync(cancellationTokenSource.Token); |
||||
|
_logger.LogInformation("命令执行结果: {IsSuccess}", result.IsSuccess); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
_logger.LogError(ex, "命令执行失败: {Command}", command); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override async Task<CommandExecutionResult> ExecuteCommandAsync(string command, CancellationTokenSource cancellationTokenSource) |
||||
|
{ |
||||
|
return await ExecuteCommandInternalAsync(command, $"/c {command}", cancellationTokenSource); |
||||
|
} |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
using CoreAgent.Domain.Interfaces.System.Command; |
||||
|
using CoreAgent.Infrastructure.Command.Executors; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Command.Factories; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 系统命令执行器工厂
|
||||
|
/// </summary>
|
||||
|
public class SystemCommandExecutorFactory : ISystemCommandExecutorFactory |
||||
|
{ |
||||
|
private readonly ILogger<WindowsCommandExecutor> _windowsLogger; |
||||
|
private readonly ILogger<LinuxCommandExecutor> _linuxLogger; |
||||
|
|
||||
|
public SystemCommandExecutorFactory( |
||||
|
ILogger<WindowsCommandExecutor> windowsLogger, |
||||
|
ILogger<LinuxCommandExecutor> linuxLogger) |
||||
|
{ |
||||
|
_windowsLogger = windowsLogger; |
||||
|
_linuxLogger = linuxLogger; |
||||
|
} |
||||
|
|
||||
|
public ISystemCommandExecutor CreateExecutor() |
||||
|
{ |
||||
|
if (OperatingSystem.IsWindows()) |
||||
|
{ |
||||
|
return new WindowsCommandExecutor(_windowsLogger); |
||||
|
} |
||||
|
else if (OperatingSystem.IsLinux()) |
||||
|
{ |
||||
|
return new LinuxCommandExecutor(_linuxLogger); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
throw new PlatformNotSupportedException("当前操作系统不支持命令执行"); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,123 +0,0 @@ |
|||||
using CliWrap; |
|
||||
using CliWrap.Buffered; |
|
||||
using CoreAgent.Domain.Interfaces; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Command; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Linux命令策略实现
|
|
||||
/// </summary>
|
|
||||
public class LinuxCommandStrategy : ICommandStrategy |
|
||||
{ |
|
||||
private readonly ILogger<LinuxCommandStrategy> _logger; |
|
||||
|
|
||||
public LinuxCommandStrategy(ILogger<LinuxCommandStrategy> logger) |
|
||||
{ |
|
||||
_logger = logger; |
|
||||
} |
|
||||
|
|
||||
public async Task ExecuteBufferedAsync(string arguments, Action<string> onOutputReceived, CancellationTokenSource source) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = Cli.Wrap("/bin/bash") |
|
||||
.WithArguments($"-c \"{arguments}\"") |
|
||||
.WithStandardOutputPipe(PipeTarget.ToDelegate(onOutputReceived)) |
|
||||
.WithStandardErrorPipe(PipeTarget.ToDelegate(onOutputReceived)) |
|
||||
.WithValidation(CommandResultValidation.None); |
|
||||
var result = await cmd.ExecuteBufferedAsync(source.Token); |
|
||||
_logger.LogInformation("{Arguments} result:{IsSuccess}", arguments, result.IsSuccess); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<string> ExecuteBufferedAsync(string arguments, CancellationTokenSource source) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = Cli.Wrap("/bin/bash") |
|
||||
.WithArguments($"-c \"{arguments}\"") |
|
||||
.WithValidation(CommandResultValidation.None); |
|
||||
var cmdResult = await cmd.ExecuteBufferedAsync(source.Token); |
|
||||
string result = string.Empty; |
|
||||
if (cmdResult.IsSuccess) |
|
||||
result = cmdResult.StandardOutput; |
|
||||
else |
|
||||
result = cmdResult.StandardError; |
|
||||
_logger.LogDebug("ExecuteAsync cmd :{Arguments} StandardOutput:{Output} :StandardError {Error}", |
|
||||
arguments, cmdResult.StandardOutput, cmdResult.StandardError); |
|
||||
return result; |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
return string.Empty; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> ExecuteAsync(string arguments, CancellationTokenSource source) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = await Cli.Wrap("/bin/bash") |
|
||||
.WithArguments($"-c \"{arguments}\"") |
|
||||
.ExecuteAsync(source.Token); |
|
||||
_logger.LogDebug("ExecuteAsync cmd :{Arguments} :result {IsSuccess}", arguments, cmd.IsSuccess); |
|
||||
return cmd.IsSuccess; |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<string> ExecuteBufferedAsync(string[] arguments, CancellationTokenSource source, string command = "/root/enb/doc/ws.js") |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = Cli.Wrap(command) |
|
||||
.WithArguments(arguments) |
|
||||
.WithValidation(CommandResultValidation.None); |
|
||||
var cmdResult = await cmd.ExecuteBufferedAsync(source.Token); |
|
||||
string result = string.Empty; |
|
||||
if (cmdResult.IsSuccess) |
|
||||
result = cmdResult.StandardOutput; |
|
||||
else |
|
||||
result = cmdResult.StandardError; |
|
||||
_logger.LogDebug("ExecuteAsync cmd :{Arguments} :result {Result}", arguments, result); |
|
||||
return result; |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
return string.Empty; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> ExecuteAsync(string[] arguments, CancellationTokenSource source, string command = "/root/enb/doc/ws.js") |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = await Cli.Wrap(command) |
|
||||
.WithArguments(arguments) |
|
||||
.WithValidation(CommandResultValidation.None) |
|
||||
.ExecuteAsync(source.Token); |
|
||||
return cmd.IsSuccess; |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,123 +0,0 @@ |
|||||
using CliWrap; |
|
||||
using CliWrap.Buffered; |
|
||||
using CoreAgent.Domain.Interfaces; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Command; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Windows命令策略实现
|
|
||||
/// </summary>
|
|
||||
public class WindowsCommandStrategy : ICommandStrategy |
|
||||
{ |
|
||||
private readonly ILogger<WindowsCommandStrategy> _logger; |
|
||||
|
|
||||
public WindowsCommandStrategy(ILogger<WindowsCommandStrategy> logger) |
|
||||
{ |
|
||||
_logger = logger; |
|
||||
} |
|
||||
|
|
||||
public async Task ExecuteBufferedAsync(string arguments, Action<string> onOutputReceived, CancellationTokenSource source) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = Cli.Wrap("cmd.exe") |
|
||||
.WithArguments($"/c {arguments}") |
|
||||
.WithStandardOutputPipe(PipeTarget.ToDelegate(onOutputReceived)) |
|
||||
.WithStandardErrorPipe(PipeTarget.ToDelegate(onOutputReceived)) |
|
||||
.WithValidation(CommandResultValidation.None); |
|
||||
var result = await cmd.ExecuteBufferedAsync(source.Token); |
|
||||
_logger.LogInformation("{Arguments} result:{IsSuccess}", arguments, result.IsSuccess); |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<string> ExecuteBufferedAsync(string arguments, CancellationTokenSource source) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = Cli.Wrap("cmd.exe") |
|
||||
.WithArguments($"/c {arguments}") |
|
||||
.WithValidation(CommandResultValidation.None); |
|
||||
var cmdResult = await cmd.ExecuteBufferedAsync(source.Token); |
|
||||
string result = string.Empty; |
|
||||
if (cmdResult.IsSuccess) |
|
||||
result = cmdResult.StandardOutput; |
|
||||
else |
|
||||
result = cmdResult.StandardError; |
|
||||
_logger.LogDebug("ExecuteAsync cmd :{Arguments} StandardOutput:{Output} :StandardError {Error}", |
|
||||
arguments, cmdResult.StandardOutput, cmdResult.StandardError); |
|
||||
return result; |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
return string.Empty; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> ExecuteAsync(string arguments, CancellationTokenSource source) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = await Cli.Wrap("cmd.exe") |
|
||||
.WithArguments($"/c {arguments}") |
|
||||
.ExecuteAsync(source.Token); |
|
||||
_logger.LogDebug("ExecuteAsync cmd :{Arguments} :result {IsSuccess}", arguments, cmd.IsSuccess); |
|
||||
return cmd.IsSuccess; |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<string> ExecuteBufferedAsync(string[] arguments, CancellationTokenSource source, string command = "powershell.exe") |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = Cli.Wrap(command) |
|
||||
.WithArguments(arguments) |
|
||||
.WithValidation(CommandResultValidation.None); |
|
||||
var cmdResult = await cmd.ExecuteBufferedAsync(source.Token); |
|
||||
string result = string.Empty; |
|
||||
if (cmdResult.IsSuccess) |
|
||||
result = cmdResult.StandardOutput; |
|
||||
else |
|
||||
result = cmdResult.StandardError; |
|
||||
_logger.LogDebug("ExecuteAsync cmd :{Arguments} :result {Result}", arguments, result); |
|
||||
return result; |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
return string.Empty; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> ExecuteAsync(string[] arguments, CancellationTokenSource source, string command = "powershell.exe") |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
_logger.LogDebug("start ExecuteAsync cmd :{Arguments}", arguments); |
|
||||
var cmd = await Cli.Wrap(command) |
|
||||
.WithArguments(arguments) |
|
||||
.WithValidation(CommandResultValidation.None) |
|
||||
.ExecuteAsync(source.Token); |
|
||||
return cmd.IsSuccess; |
|
||||
} |
|
||||
catch (Exception ex) |
|
||||
{ |
|
||||
_logger.LogError(ex, "ExecuteAsync cmd {Arguments} failed", arguments); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,13 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
|
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>net8.0</TargetFramework> |
|
||||
<ImplicitUsings>enable</ImplicitUsings> |
|
||||
<Nullable>enable</Nullable> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="Serilog" Version="4.3.0" /> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
</Project> |
|
@ -1,44 +0,0 @@ |
|||||
using CoreAgent.Infrastructure.Options; |
|
||||
using Microsoft.AspNetCore.Builder; |
|
||||
using Microsoft.AspNetCore.Mvc; |
|
||||
using Microsoft.AspNetCore.Mvc.Versioning; |
|
||||
using Microsoft.Extensions.Configuration; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Extensions |
|
||||
{ |
|
||||
public static class ApiVersioningExtensions |
|
||||
{ |
|
||||
public static IServiceCollection AddApiVersioningExtension(this IServiceCollection services, IConfiguration config) |
|
||||
{ |
|
||||
var settings = config.GetSection("ApiVersioning").Get<ApiVersioningSettings>() ?? new ApiVersioningSettings(); |
|
||||
|
|
||||
services.AddApiVersioning(options => |
|
||||
{ |
|
||||
options.DefaultApiVersion = ApiVersion.Parse(settings.DefaultVersion); |
|
||||
options.AssumeDefaultVersionWhenUnspecified = settings.AssumeDefaultVersionWhenUnspecified; |
|
||||
options.ReportApiVersions = settings.ReportApiVersions; |
|
||||
options.ApiVersionReader = ApiVersionReader.Combine( |
|
||||
settings.ShowVersionInUrl ? new UrlSegmentApiVersionReader() : null, |
|
||||
settings.ShowVersionInQueryString ? new QueryStringApiVersionReader("api-version") : null, |
|
||||
settings.ShowVersionInHeader ? new HeaderApiVersionReader("api-version") : null, |
|
||||
settings.ShowVersionInMediaType ? new MediaTypeApiVersionReader("version") : null |
|
||||
); |
|
||||
}); |
|
||||
|
|
||||
services.AddVersionedApiExplorer(options => |
|
||||
{ |
|
||||
options.GroupNameFormat = "'v'VVV"; |
|
||||
options.SubstituteApiVersionInUrl = true; |
|
||||
}); |
|
||||
|
|
||||
return services; |
|
||||
} |
|
||||
|
|
||||
public static IApplicationBuilder UseApiVersioningExtension(this IApplicationBuilder app) |
|
||||
{ |
|
||||
// 可以在这里添加版本控制相关的中间件
|
|
||||
return app; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,13 +0,0 @@ |
|||||
using CoreAgent.Infrastructure.Middleware; |
|
||||
using Microsoft.AspNetCore.Builder; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Extensions |
|
||||
{ |
|
||||
public static class ExceptionMiddlewareExtensions |
|
||||
{ |
|
||||
public static IApplicationBuilder UseExceptionMiddleware(this IApplicationBuilder builder) |
|
||||
{ |
|
||||
return builder.UseMiddleware<ExceptionMiddleware>(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,35 @@ |
|||||
|
using CoreAgent.Infrastructure.Logging; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Serilog; |
||||
|
using Serilog.Events; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Extensions.Logging; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 日志扩展方法
|
||||
|
/// </summary>
|
||||
|
public static class LoggingExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 添加日志服务
|
||||
|
/// </summary>
|
||||
|
/// <param name="services">服务集合</param>
|
||||
|
/// <returns>服务集合</returns>
|
||||
|
public static IServiceCollection AddLoggingService(this IServiceCollection services, IConfiguration configuration) |
||||
|
{ |
||||
|
// 初始化应用程序日志记录器
|
||||
|
ApplicationLogger.Initialize(configuration); |
||||
|
Log.Information("Application starting..."); |
||||
|
|
||||
|
// 添加 Serilog 到依赖注入容器
|
||||
|
services.AddLogging(loggingBuilder => |
||||
|
{ |
||||
|
loggingBuilder.ClearProviders(); |
||||
|
loggingBuilder.AddSerilog(dispose: true); |
||||
|
}); |
||||
|
|
||||
|
return services; |
||||
|
} |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
|
||||
|
using CoreAgent.Infrastructure.Options; |
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Serilog; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Extensions.Logging; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 请求日志扩展方法
|
||||
|
/// </summary>
|
||||
|
public static class RequestLoggingExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 添加请求日志中间件
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">应用程序构建器</param>
|
||||
|
/// <returns>应用程序构建器</returns>
|
||||
|
|
||||
|
public static IServiceCollection AddRequestLogging(this IServiceCollection services, IConfiguration config) |
||||
|
{ |
||||
|
services.Configure<RequestLoggingOptions>(config.GetSection("RequestLogging")); |
||||
|
return services; |
||||
|
} |
||||
|
|
||||
|
public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app, IConfiguration config) |
||||
|
{ |
||||
|
app.UseMiddleware<RequestLoggingMiddleware>(); |
||||
|
app.UseMiddleware<ResponseLoggingMiddleware>(); |
||||
|
return app; |
||||
|
} |
||||
|
} |
@ -0,0 +1,112 @@ |
|||||
|
using System.Diagnostics; |
||||
|
using CoreAgent.Infrastructure.Options; |
||||
|
using System.Text; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Extensions.Logging; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 请求日志中间件
|
||||
|
/// </summary>
|
||||
|
public class RequestLoggingMiddleware |
||||
|
{ |
||||
|
private readonly RequestDelegate _next; |
||||
|
private readonly ILogger<RequestLoggingMiddleware> _logger; |
||||
|
private readonly RequestLoggingOptions _options; |
||||
|
|
||||
|
public RequestLoggingMiddleware( |
||||
|
RequestDelegate next, |
||||
|
ILogger<RequestLoggingMiddleware> logger, |
||||
|
IOptions<RequestLoggingOptions> options) |
||||
|
{ |
||||
|
_next = next; |
||||
|
_logger = logger; |
||||
|
_options = options.Value; |
||||
|
} |
||||
|
|
||||
|
public async Task InvokeAsync(HttpContext context) |
||||
|
{ |
||||
|
if (ShouldSkipLogging(context)) |
||||
|
{ |
||||
|
await _next(context); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var sw = Stopwatch.StartNew(); |
||||
|
var requestBody = string.Empty; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
if (_options.LogRequestBody && context.Request.Body != null) |
||||
|
{ |
||||
|
requestBody = await ReadRequestBodyAsync(context.Request); |
||||
|
} |
||||
|
|
||||
|
_logger.LogInformation( |
||||
|
"Request started: {Method} {Path} {QueryString} {RequestBody}", |
||||
|
context.Request.Method, |
||||
|
context.Request.Path, |
||||
|
context.Request.QueryString, |
||||
|
requestBody); |
||||
|
|
||||
|
await _next(context); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
sw.Stop(); |
||||
|
_logger.LogInformation( |
||||
|
"Request completed: {Method} {Path} in {ElapsedMilliseconds}ms with status code {StatusCode}", |
||||
|
context.Request.Method, |
||||
|
context.Request.Path, |
||||
|
sw.ElapsedMilliseconds, |
||||
|
context.Response.StatusCode); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private bool ShouldSkipLogging(HttpContext context) |
||||
|
{ |
||||
|
if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (_options.ExcludeMethods.Contains(context.Request.Method)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private async Task<string> ReadRequestBodyAsync(HttpRequest request) |
||||
|
{ |
||||
|
if (!request.Body.CanRead || !request.Body.CanSeek) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
var originalPosition = request.Body.Position; |
||||
|
request.Body.Position = 0; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
using var reader = new StreamReader( |
||||
|
request.Body, |
||||
|
encoding: Encoding.UTF8, |
||||
|
detectEncodingFromByteOrderMarks: false, |
||||
|
bufferSize: 1024 * 45, |
||||
|
leaveOpen: true); |
||||
|
|
||||
|
var body = await reader.ReadToEndAsync(); |
||||
|
return body.Length > _options.MaxRequestBodySize |
||||
|
? body.Substring(0, _options.MaxRequestBodySize) + "..." |
||||
|
: body; |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
request.Body.Position = originalPosition; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,106 @@ |
|||||
|
using System.Diagnostics; |
||||
|
using CoreAgent.Infrastructure.Options; |
||||
|
using System.Text; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Extensions.Logging; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 响应日志中间件
|
||||
|
/// </summary>
|
||||
|
public class ResponseLoggingMiddleware |
||||
|
{ |
||||
|
private readonly RequestDelegate _next; |
||||
|
private readonly ILogger<ResponseLoggingMiddleware> _logger; |
||||
|
private readonly RequestLoggingOptions _options; |
||||
|
|
||||
|
public ResponseLoggingMiddleware( |
||||
|
RequestDelegate next, |
||||
|
ILogger<ResponseLoggingMiddleware> logger, |
||||
|
IOptions<RequestLoggingOptions> options) |
||||
|
{ |
||||
|
_next = next; |
||||
|
_logger = logger; |
||||
|
_options = options.Value; |
||||
|
} |
||||
|
|
||||
|
public async Task InvokeAsync(HttpContext context) |
||||
|
{ |
||||
|
if (ShouldSkipLogging(context)) |
||||
|
{ |
||||
|
await _next(context); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 保存原始响应流
|
||||
|
var originalBodyStream = context.Response.Body; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
// 创建一个新的内存流来捕获响应
|
||||
|
using var responseBody = new MemoryStream(); |
||||
|
context.Response.Body = responseBody; |
||||
|
|
||||
|
// 继续处理请求
|
||||
|
await _next(context); |
||||
|
|
||||
|
// 读取响应内容
|
||||
|
responseBody.Seek(0, SeekOrigin.Begin); |
||||
|
var response = await ReadResponseBodyAsync(responseBody); |
||||
|
|
||||
|
// 记录响应
|
||||
|
_logger.LogInformation( |
||||
|
"Response for {Method} {Path}: {StatusCode} - {Response}", |
||||
|
context.Request.Method, |
||||
|
context.Request.Path, |
||||
|
context.Response.StatusCode, |
||||
|
response); |
||||
|
|
||||
|
// 将响应写回原始流
|
||||
|
responseBody.Seek(0, SeekOrigin.Begin); |
||||
|
await responseBody.CopyToAsync(originalBodyStream); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
// 确保响应体被恢复
|
||||
|
context.Response.Body = originalBodyStream; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private bool ShouldSkipLogging(HttpContext context) |
||||
|
{ |
||||
|
if (!_options.LogResponseBody) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (_options.ExcludeMethods.Contains(context.Request.Method)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private async Task<string> ReadResponseBodyAsync(Stream responseBody) |
||||
|
{ |
||||
|
using var reader = new StreamReader( |
||||
|
responseBody, |
||||
|
encoding: Encoding.UTF8, |
||||
|
detectEncodingFromByteOrderMarks: false, |
||||
|
bufferSize: 1024 * 45, |
||||
|
leaveOpen: true); |
||||
|
|
||||
|
var body = await reader.ReadToEndAsync(); |
||||
|
return body.Length > _options.MaxResponseBodySize |
||||
|
? body.Substring(0, _options.MaxResponseBodySize) + "..." |
||||
|
: body; |
||||
|
} |
||||
|
} |
@ -1,27 +0,0 @@ |
|||||
using CoreAgent.Infrastructure.Logging; |
|
||||
using Microsoft.Extensions.Configuration; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Serilog; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Extensions |
|
||||
{ |
|
||||
public static class LoggingExtensions |
|
||||
{ |
|
||||
public static IServiceCollection AddLoggingServices(this IServiceCollection services, IConfiguration configuration) |
|
||||
{ |
|
||||
// 初始化应用程序日志记录器
|
|
||||
ApplicationLogger.Initialize(configuration); |
|
||||
Log.Information("Application starting..."); |
|
||||
|
|
||||
// 添加 Serilog 到依赖注入容器
|
|
||||
services.AddLogging(loggingBuilder => |
|
||||
{ |
|
||||
loggingBuilder.ClearProviders(); |
|
||||
loggingBuilder.AddSerilog(dispose: true); |
|
||||
}); |
|
||||
|
|
||||
return services; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,24 +0,0 @@ |
|||||
using CoreAgent.Infrastructure.Middleware; |
|
||||
using CoreAgent.Infrastructure.Options; |
|
||||
using Microsoft.AspNetCore.Builder; |
|
||||
using Microsoft.Extensions.Configuration; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Extensions |
|
||||
{ |
|
||||
public static class RequestLoggingExtensions |
|
||||
{ |
|
||||
public static IServiceCollection AddRequestLogging(this IServiceCollection services, IConfiguration config) |
|
||||
{ |
|
||||
services.Configure<RequestLoggingOptions>(config.GetSection("RequestLogging")); |
|
||||
return services; |
|
||||
} |
|
||||
|
|
||||
public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder app, IConfiguration config) |
|
||||
{ |
|
||||
app.UseMiddleware<RequestLoggingMiddleware>(); |
|
||||
app.UseMiddleware<ResponseLoggingMiddleware>(); |
|
||||
return app; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,51 @@ |
|||||
|
using CoreAgent.Infrastructure.Options; |
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Versioning; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Extensions.ServiceCollection; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// API版本服务扩展方法
|
||||
|
/// </summary>
|
||||
|
public static class ApiVersioningServiceExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 添加API版本服务
|
||||
|
/// </summary>
|
||||
|
/// <param name="services">服务集合</param>
|
||||
|
/// <returns>服务集合</returns>
|
||||
|
public static IServiceCollection AddApiVersioningService(this IServiceCollection services, IConfiguration config) |
||||
|
{ |
||||
|
var settings = config.GetSection("ApiVersioning").Get<ApiVersioningSettings>() ?? new ApiVersioningSettings(); |
||||
|
|
||||
|
services.AddApiVersioning(options => |
||||
|
{ |
||||
|
options.DefaultApiVersion = ApiVersion.Parse(settings.DefaultVersion); |
||||
|
options.AssumeDefaultVersionWhenUnspecified = settings.AssumeDefaultVersionWhenUnspecified; |
||||
|
options.ReportApiVersions = settings.ReportApiVersions; |
||||
|
options.ApiVersionReader = ApiVersionReader.Combine( |
||||
|
settings.ShowVersionInUrl ? new UrlSegmentApiVersionReader() : null, |
||||
|
settings.ShowVersionInQueryString ? new QueryStringApiVersionReader("api-version") : null, |
||||
|
settings.ShowVersionInHeader ? new HeaderApiVersionReader("api-version") : null, |
||||
|
settings.ShowVersionInMediaType ? new MediaTypeApiVersionReader("version") : null |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
services.AddVersionedApiExplorer(options => |
||||
|
{ |
||||
|
options.GroupNameFormat = "'v'VVV"; |
||||
|
options.SubstituteApiVersionInUrl = true; |
||||
|
}); |
||||
|
|
||||
|
return services; |
||||
|
} |
||||
|
|
||||
|
public static IApplicationBuilder UseApiVersioningExtension(this IApplicationBuilder app) |
||||
|
{ |
||||
|
// 可以在这里添加版本控制相关的中间件
|
||||
|
return app; |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
using CoreAgent.Domain.Interfaces.Network; |
||||
|
using CoreAgent.Domain.Interfaces.System.Command; |
||||
|
using CoreAgent.Infrastructure.Command.Factories; |
||||
|
using CoreAgent.Infrastructure.Services; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Extensions.ServiceCollection; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 命令服务扩展方法
|
||||
|
/// </summary>
|
||||
|
public static class CommandServiceExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 添加命令策略服务
|
||||
|
/// </summary>
|
||||
|
/// <param name="services">服务集合</param>
|
||||
|
/// <returns>服务集合</returns>
|
||||
|
public static IServiceCollection AddCommandCustomService(this IServiceCollection services) |
||||
|
{ |
||||
|
// 注册命令执行器工厂
|
||||
|
services.AddSingleton<ISystemCommandExecutorFactory, SystemCommandExecutorFactory>(); |
||||
|
|
||||
|
// 注册命令执行器
|
||||
|
services.AddTransient<ISystemCommandExecutor>(sp => |
||||
|
{ |
||||
|
var factory = sp.GetRequiredService<ISystemCommandExecutorFactory>(); |
||||
|
return factory.CreateExecutor(); |
||||
|
}); |
||||
|
|
||||
|
// 注册 ICellularNetworkService
|
||||
|
services.AddScoped<ICellularNetworkService, CellularNetworkService>(); |
||||
|
return services; |
||||
|
} |
||||
|
} |
@ -1,27 +0,0 @@ |
|||||
using CoreAgent.Domain.Interfaces; |
|
||||
using CoreAgent.Infrastructure.Command; |
|
||||
using CoreAgent.Infrastructure.Services; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Extensions; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 服务集合扩展方法
|
|
||||
/// </summary>
|
|
||||
public static class ServiceCollectionExtensions |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 添加命令策略服务
|
|
||||
/// </summary>
|
|
||||
/// <param name="services">服务集合</param>
|
|
||||
/// <returns>服务集合</returns>
|
|
||||
public static IServiceCollection AddCommandCustomService(this IServiceCollection services) |
|
||||
{ |
|
||||
// 注册命令策略工厂
|
|
||||
services.AddSingleton<ICommandStrategyFactory, CommandStrategyFactory>(); |
|
||||
|
|
||||
// 注册 ICellularNetworkService
|
|
||||
services.AddScoped<ICellularNetworkService, CellularNetworkService>(); |
|
||||
return services; |
|
||||
} |
|
||||
} |
|
@ -0,0 +1,20 @@ |
|||||
|
using CoreAgent.Infrastructure.Middleware.GlobalException; |
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
|
||||
|
namespace CoreAgent.Infrastructure.Middleware.GlobalException; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 全局异常中间件扩展方法
|
||||
|
/// </summary>
|
||||
|
public static class GlobalExceptionMiddlewareExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 使用全局异常处理中间件
|
||||
|
/// </summary>
|
||||
|
/// <param name="app">应用程序构建器</param>
|
||||
|
/// <returns>应用程序构建器</returns>
|
||||
|
public static IApplicationBuilder UseGlobalExceptionMiddleware(this IApplicationBuilder app) |
||||
|
{ |
||||
|
return app.UseMiddleware<GlobalExceptionMiddleware>(); |
||||
|
} |
||||
|
} |
@ -1,110 +0,0 @@ |
|||||
using CoreAgent.Infrastructure.Options; |
|
||||
using Microsoft.AspNetCore.Http; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using System.Diagnostics; |
|
||||
using System.Text; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Middleware |
|
||||
{ |
|
||||
public class RequestLoggingMiddleware |
|
||||
{ |
|
||||
private readonly RequestDelegate _next; |
|
||||
private readonly ILogger<RequestLoggingMiddleware> _logger; |
|
||||
private readonly RequestLoggingOptions _options; |
|
||||
|
|
||||
public RequestLoggingMiddleware( |
|
||||
RequestDelegate next, |
|
||||
ILogger<RequestLoggingMiddleware> logger, |
|
||||
IOptions<RequestLoggingOptions> options) |
|
||||
{ |
|
||||
_next = next; |
|
||||
_logger = logger; |
|
||||
_options = options.Value; |
|
||||
} |
|
||||
|
|
||||
public async Task InvokeAsync(HttpContext context) |
|
||||
{ |
|
||||
if (ShouldSkipLogging(context)) |
|
||||
{ |
|
||||
await _next(context); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
var sw = Stopwatch.StartNew(); |
|
||||
var requestBody = string.Empty; |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
if (_options.LogRequestBody && context.Request.Body != null) |
|
||||
{ |
|
||||
requestBody = await ReadRequestBodyAsync(context.Request); |
|
||||
} |
|
||||
|
|
||||
_logger.LogInformation( |
|
||||
"Request started: {Method} {Path} {QueryString} {RequestBody}", |
|
||||
context.Request.Method, |
|
||||
context.Request.Path, |
|
||||
context.Request.QueryString, |
|
||||
requestBody); |
|
||||
|
|
||||
await _next(context); |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
sw.Stop(); |
|
||||
_logger.LogInformation( |
|
||||
"Request completed: {Method} {Path} in {ElapsedMilliseconds}ms with status code {StatusCode}", |
|
||||
context.Request.Method, |
|
||||
context.Request.Path, |
|
||||
sw.ElapsedMilliseconds, |
|
||||
context.Response.StatusCode); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private bool ShouldSkipLogging(HttpContext context) |
|
||||
{ |
|
||||
if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
if (_options.ExcludeMethods.Contains(context.Request.Method)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
private async Task<string> ReadRequestBodyAsync(HttpRequest request) |
|
||||
{ |
|
||||
if (!request.Body.CanRead || !request.Body.CanSeek) |
|
||||
{ |
|
||||
return string.Empty; |
|
||||
} |
|
||||
|
|
||||
var originalPosition = request.Body.Position; |
|
||||
request.Body.Position = 0; |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
using var reader = new StreamReader( |
|
||||
request.Body, |
|
||||
encoding: Encoding.UTF8, |
|
||||
detectEncodingFromByteOrderMarks: false, |
|
||||
bufferSize: 1024 * 45, |
|
||||
leaveOpen: true); |
|
||||
|
|
||||
var body = await reader.ReadToEndAsync(); |
|
||||
return body.Length > _options.MaxRequestBodySize |
|
||||
? body.Substring(0, _options.MaxRequestBodySize) + "..." |
|
||||
: body; |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
request.Body.Position = originalPosition; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,103 +0,0 @@ |
|||||
using CoreAgent.Infrastructure.Options; |
|
||||
using Microsoft.AspNetCore.Http; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using System.Text; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Middleware |
|
||||
{ |
|
||||
public class ResponseLoggingMiddleware |
|
||||
{ |
|
||||
private readonly RequestDelegate _next; |
|
||||
private readonly ILogger<ResponseLoggingMiddleware> _logger; |
|
||||
private readonly RequestLoggingOptions _options; |
|
||||
|
|
||||
public ResponseLoggingMiddleware( |
|
||||
RequestDelegate next, |
|
||||
ILogger<ResponseLoggingMiddleware> logger, |
|
||||
IOptions<RequestLoggingOptions> options) |
|
||||
{ |
|
||||
_next = next; |
|
||||
_logger = logger; |
|
||||
_options = options.Value; |
|
||||
} |
|
||||
|
|
||||
public async Task InvokeAsync(HttpContext context) |
|
||||
{ |
|
||||
if (ShouldSkipLogging(context)) |
|
||||
{ |
|
||||
await _next(context); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 保存原始响应流
|
|
||||
var originalBodyStream = context.Response.Body; |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
// 创建一个新的内存流来捕获响应
|
|
||||
using var responseBody = new MemoryStream(); |
|
||||
context.Response.Body = responseBody; |
|
||||
|
|
||||
// 继续处理请求
|
|
||||
await _next(context); |
|
||||
|
|
||||
// 读取响应内容
|
|
||||
responseBody.Seek(0, SeekOrigin.Begin); |
|
||||
var response = await ReadResponseBodyAsync(responseBody); |
|
||||
|
|
||||
// 记录响应
|
|
||||
_logger.LogInformation( |
|
||||
"Response for {Method} {Path}: {StatusCode} - {Response}", |
|
||||
context.Request.Method, |
|
||||
context.Request.Path, |
|
||||
context.Response.StatusCode, |
|
||||
response); |
|
||||
|
|
||||
// 将响应写回原始流
|
|
||||
responseBody.Seek(0, SeekOrigin.Begin); |
|
||||
await responseBody.CopyToAsync(originalBodyStream); |
|
||||
} |
|
||||
finally |
|
||||
{ |
|
||||
// 确保响应体被恢复
|
|
||||
context.Response.Body = originalBodyStream; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private bool ShouldSkipLogging(HttpContext context) |
|
||||
{ |
|
||||
if (!_options.LogResponseBody) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p))) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
if (_options.ExcludeMethods.Contains(context.Request.Method)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
private async Task<string> ReadResponseBodyAsync(Stream responseBody) |
|
||||
{ |
|
||||
using var reader = new StreamReader( |
|
||||
responseBody, |
|
||||
encoding: Encoding.UTF8, |
|
||||
detectEncodingFromByteOrderMarks: false, |
|
||||
bufferSize: 1024 * 45, |
|
||||
leaveOpen: true); |
|
||||
|
|
||||
var body = await reader.ReadToEndAsync(); |
|
||||
return body.Length > _options.MaxResponseBodySize |
|
||||
? body.Substring(0, _options.MaxResponseBodySize) + "..." |
|
||||
: body; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,168 +0,0 @@ |
|||||
using System.Numerics; |
|
||||
using CoreAgent.Domain.Interfaces; |
|
||||
|
|
||||
namespace CoreAgent.Infrastructure.Network; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 提供IP地址相关的工具方法
|
|
||||
/// </summary>
|
|
||||
public static class IPAddressUtils |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// 检查两个IPv4地址是否在同一个子网内
|
|
||||
/// </summary>
|
|
||||
/// <param name="ip1">第一个IP地址</param>
|
|
||||
/// <param name="ip2">第二个IP地址</param>
|
|
||||
/// <param name="subnetMask">子网掩码</param>
|
|
||||
/// <returns>如果两个IP地址在同一个子网内返回true,否则返回false</returns>
|
|
||||
/// <exception cref="ArgumentException">当IP地址或子网掩码不是IPv4格式时抛出</exception>
|
|
||||
public static bool IsSameIPv4Subnet(System.Net.IPAddress ip1, System.Net.IPAddress ip2, System.Net.IPAddress subnetMask) |
|
||||
{ |
|
||||
if (ip1.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork || |
|
||||
ip2.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork || |
|
||||
subnetMask.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) |
|
||||
{ |
|
||||
throw new ArgumentException("IP addresses and subnet mask must be IPv4."); |
|
||||
} |
|
||||
|
|
||||
byte[] ip1Bytes = ip1.GetAddressBytes(); |
|
||||
byte[] ip2Bytes = ip2.GetAddressBytes(); |
|
||||
byte[] subnetBytes = subnetMask.GetAddressBytes(); |
|
||||
|
|
||||
byte[] network1 = new byte[4]; |
|
||||
byte[] network2 = new byte[4]; |
|
||||
|
|
||||
for (int i = 0; i < 4; i++) |
|
||||
{ |
|
||||
network1[i] = (byte)(ip1Bytes[i] & subnetBytes[i]); |
|
||||
network2[i] = (byte)(ip2Bytes[i] & subnetBytes[i]); |
|
||||
} |
|
||||
|
|
||||
return new System.Net.IPAddress(network1).Equals(new System.Net.IPAddress(network2)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 检查两个IPv6地址是否在同一个子网内
|
|
||||
/// </summary>
|
|
||||
/// <param name="ip1">第一个IP地址</param>
|
|
||||
/// <param name="ip2">第二个IP地址</param>
|
|
||||
/// <param name="prefixLength">前缀长度</param>
|
|
||||
/// <returns>如果两个IP地址在同一个子网内返回true,否则返回false</returns>
|
|
||||
/// <exception cref="ArgumentException">当IP地址不是IPv6格式时抛出</exception>
|
|
||||
public static bool IsSameIPv6Subnet(System.Net.IPAddress ip1, System.Net.IPAddress ip2, int prefixLength) |
|
||||
{ |
|
||||
if (ip1.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6 || |
|
||||
ip2.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6) |
|
||||
{ |
|
||||
throw new ArgumentException("IP addresses must be IPv6."); |
|
||||
} |
|
||||
|
|
||||
BigInteger ip1Value = new BigInteger(ip1.GetAddressBytes()); |
|
||||
BigInteger ip2Value = new BigInteger(ip2.GetAddressBytes()); |
|
||||
|
|
||||
BigInteger mask = BigInteger.One << (128 - prefixLength); |
|
||||
mask -= BigInteger.One; |
|
||||
mask = ~mask; |
|
||||
|
|
||||
return (ip1Value & mask) == (ip2Value & mask); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 获取系统的IPv4地址列表
|
|
||||
/// </summary>
|
|
||||
/// <param name="commandStrategyFactory">命令策略工厂</param>
|
|
||||
/// <param name="source">取消令牌源</param>
|
|
||||
/// <param name="logger">日志记录器</param>
|
|
||||
/// <returns>IPv4地址列表</returns>
|
|
||||
public static async Task<IEnumerable<string>> CheckAddressIPv4(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source) |
|
||||
{ |
|
||||
List<string> addrs = new List<string>(); |
|
||||
var inet4result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet ' | awk '{print $2}'", source); |
|
||||
if (!string.IsNullOrWhiteSpace(inet4result)) |
|
||||
addrs.AddRange(RemoveNewLines(inet4result)); |
|
||||
return addrs; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 获取系统的IPv6地址列表
|
|
||||
/// </summary>
|
|
||||
/// <param name="commandStrategyFactory">命令策略工厂</param>
|
|
||||
/// <param name="source">取消令牌源</param>
|
|
||||
/// <param name="logger">日志记录器</param>
|
|
||||
/// <returns>IPv6地址列表</returns>
|
|
||||
public static async Task<IEnumerable<string>> CheckAddressIPv6(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source) |
|
||||
{ |
|
||||
List<string> addrs = new List<string>(); |
|
||||
var inet6result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet6 ' | awk '{print $2}'", source); |
|
||||
if (!string.IsNullOrWhiteSpace(inet6result)) |
|
||||
addrs.AddRange(RemoveNewLines(inet6result)); |
|
||||
return addrs; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 获取系统的IPv4地址和子网掩码列表
|
|
||||
/// </summary>
|
|
||||
/// <param name="commandStrategyFactory">命令策略工厂</param>
|
|
||||
/// <param name="source">取消令牌源</param>
|
|
||||
/// <param name="logger">日志记录器</param>
|
|
||||
/// <returns>IPv4地址和子网掩码的元组列表</returns>
|
|
||||
public static async Task<List<(string ipv4, string mask)>> CheckAddressIPv4Mask(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source) |
|
||||
{ |
|
||||
List<(string ipv4, string mask)> addrs = new List<(string ipv4, string mask)>(); |
|
||||
var inet4result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet ' | awk '{print $2\\\"|\\\"$4}'", source); |
|
||||
if (!string.IsNullOrWhiteSpace(inet4result)) |
|
||||
addrs.AddRange(RemoveNewLines(inet4result).Select(s => (s.Split("|")[0], s.Split("|")[1]))); |
|
||||
return addrs; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 获取系统的IPv6地址和前缀长度列表
|
|
||||
/// </summary>
|
|
||||
/// <param name="commandStrategyFactory">命令策略工厂</param>
|
|
||||
/// <param name="source">取消令牌源</param>
|
|
||||
/// <param name="logger">日志记录器</param>
|
|
||||
/// <returns>IPv6地址和前缀长度的元组列表</returns>
|
|
||||
public static async Task<List<(string ipv6, string Prefix)>> CheckAddressIPv6Prefix(ICommandStrategyFactory commandStrategyFactory, CancellationTokenSource source) |
|
||||
{ |
|
||||
List<(string ipv6, string Prefix)> addrs = new List<(string ipv6, string Prefix)>(); |
|
||||
var inet6result = await commandStrategyFactory.CreateStrategy().ExecuteBufferedAsync("ifconfig | grep 'inet6 ' | awk '{print $2\\\"|\\\"$4}'", source); |
|
||||
if (!string.IsNullOrWhiteSpace(inet6result)) |
|
||||
addrs.AddRange(RemoveNewLines(inet6result).Select(s => (s.Split("|")[0], s.Split("|")[1]))); |
|
||||
return addrs; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 计算两个IP地址的匹配位数
|
|
||||
/// </summary>
|
|
||||
/// <param name="ip1">第一个IP地址</param>
|
|
||||
/// <param name="ip2">第二个IP地址</param>
|
|
||||
/// <returns>匹配的位数</returns>
|
|
||||
public static int GetMatchingBits(string ip1, string ip2) |
|
||||
{ |
|
||||
byte[] bytes1 = System.Net.IPAddress.Parse(ip1).GetAddressBytes(); |
|
||||
byte[] bytes2 = System.Net.IPAddress.Parse(ip2).GetAddressBytes(); |
|
||||
int matchingBits = 0; |
|
||||
for (int i = 0; i < bytes1.Length; i++) |
|
||||
{ |
|
||||
int diff = bytes1[i] ^ bytes2[i]; // 异或计算不同位
|
|
||||
for (int bit = 7; bit >= 0; bit--) |
|
||||
{ |
|
||||
if ((diff & (1 << bit)) == 0) |
|
||||
matchingBits++; |
|
||||
else |
|
||||
return matchingBits; |
|
||||
} |
|
||||
} |
|
||||
return matchingBits; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 移除字符串中的换行符并分割成数组
|
|
||||
/// </summary>
|
|
||||
/// <param name="input">输入字符串</param>
|
|
||||
/// <returns>分割后的字符串数组</returns>
|
|
||||
private static string[] RemoveNewLines(string input) |
|
||||
{ |
|
||||
return input.Split("\n", StringSplitOptions.RemoveEmptyEntries); |
|
||||
} |
|
||||
} |
|
Loading…
Reference in new issue