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>
|
|||
/// 蜂窝网络配置
|
@ -1,4 +1,4 @@ |
|||
namespace CoreAgent.Domain.Models; |
|||
namespace CoreAgent.Domain.Models.Network; |
|||
|
|||
/// <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