using Dpz.Core.Backup;
using Dpz.Core.Service.Mediator.Features.Storage.Commands;
using Dpz.Core.WebApi.Models.Request;
using Microsoft.AspNetCore.RateLimiting;
namespace Dpz.Core.WebApi.Controllers;
/// <summary>
/// 系统设置
/// </summary>
[ApiController, Route("api/[controller]")]
public class SysController(
IRestoreService restoreService,
ILogger<SysController> logger,
IMediator mediator,
IConfiguration configuration
) : ControllerBase
{
/// <summary>
/// 还原最新数据
/// </summary>
/// <returns></returns>
[HttpPost("restore"), Authorize(nameof(Permissions.System))]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> Restore([FromForm] RestoreRequest request)
{
try
{
var restoreFilePath = await SaveAsAsync(request.RestoreFile);
await restoreService.RestoreAsync(
request.ConnectionString,
restoreFilePath,
request.FilePassword
);
}
catch (Exception e)
{
logger.LogError(e, "restore fail");
return BadRequest();
}
return NoContent();
}
private static async Task<string> SaveAsAsync(IFormFile file)
{
if (file.Length <= 0)
{
throw new ArgumentException($"{nameof(file)} length must be greater than 0");
}
var restorePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "restore");
if (!Directory.Exists(restorePath))
{
Directory.CreateDirectory(restorePath);
}
await using var stream = file.OpenReadStream();
var restoreFilePath = Path.Combine(restorePath, $"{DateTime.Now:yyyyMMdd}.zip");
await using var fileStream = new FileStream(
restoreFilePath,
FileMode.Create,
FileAccess.Write,
FileShare.Write,
1 << 12
);
await stream.CopyToAsync(fileStream);
await fileStream.FlushAsync();
return restoreFilePath;
}
/// <summary>
/// 接收腾讯云媒体处理回调
/// </summary>
[HttpPost("receive/tencent/media-process")]
[AllowAnonymous]
[EnableRateLimiting("callback")]
[Consumes("application/json", "application/xml", "text/xml")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> ReceiveTencentMediaProcessCallback(
[FromQuery] string token = "",
CancellationToken cancellationToken = default
)
{
if (!IsCallbackTokenValid(token))
{
return Unauthorized("token invalid");
}
var result = await mediator.Send(
new TencentMediaProcessCallbackRequest { Request = Request },
cancellationToken
);
if (result.Success)
{
return Ok();
}
var statusCode = result.Code is >= 400 and <= 599
? result.Code
: StatusCodes.Status400BadRequest;
return StatusCode(statusCode, result);
}
/// <summary>
/// 校验回调令牌
/// </summary>
private bool IsCallbackTokenValid(string token)
{
var configuredToken = configuration["TencentCallbackToken"];
if (string.IsNullOrWhiteSpace(configuredToken) || string.IsNullOrWhiteSpace(token))
{
return false;
}
var configuredHash = SHA256.HashData(Encoding.UTF8.GetBytes(configuredToken));
var providedHash = SHA256.HashData(Encoding.UTF8.GetBytes(token));
return CryptographicOperations.FixedTimeEquals(configuredHash, providedHash);
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个 ASP.NET Core Web API 控制器,名为 SysController,用于处理系统级别的操作。以下是详细解释:
控制器概述
- 路由:
api/Sys - 用途: 提供系统设置相关的 API 端点
- 依赖注入: 使用主构造函数注入了4个依赖项
IRestoreService: 数据恢复服务ILogger<SysController>: 日志记录器IMediator: 中介者模式实现(可能用于 CQRS)IConfiguration: 配置访问接口
主要功能
1. Restore 方法 - 数据还原功能
[HttpPost("restore"), Authorize(nameof(Permissions.System))]
- 端点:
POST /api/Sys/restore - 权限: 需要系统权限认证
- 功能: 从上传的备份文件还原数据
- 流程:
- 接收包含备份文件的表单数据
- 将上传的文件保存到本地
restore目录 - 调用
restoreService执行还原操作 - 异常处理:记录日志并返回 400 错误
2. SaveAsAsync 方法 - 文件保存辅助方法
- 私有静态方法,用于保存上传的文件
- 功能:
- 验证文件大小必须大于 0
- 创建
restore目录(如果不存在) - 将文件保存为
yyyyMMdd.zip格式(按日期命名) - 使用 4KB 缓冲区进行文件流操作
3. ReceiveTencentMediaProcessCallback 方法 - 腾讯云媒体处理回调接口
[HttpPost("receive/tencent/media-process")]
[AllowAnonymous]
[EnableRateLimiting("callback")]
- 端点:
POST /api/Sys/receive/tencent/media-process - 特点:
- 允许匿名访问(不需要认证)
- 启用了名为 "callback" 的限流策略
- 支持多种内容类型:JSON、XML
- 安全机制: 通过查询参数
token进行身份验证 - 流程:
- 验证回调令牌(token)
- 使用 Mediator 模式处理请求
- 根据处理结果返回相应的 HTTP 状态码
4. IsCallbackTokenValid 方法 - 令牌验证
- 安全实现:
- 从配置中读取预设的令牌
- 使用 SHA256 对令牌进行哈希
- 使用
CryptographicOperations.FixedTimeEquals进行恒定时间比较,防止时序攻击
- 验证逻辑: 只有当配置的令牌和提供的令牌都非空且哈希值相等时才通过验证
技术亮点
- 使用主构造函数 (C# 12 特性) 简化依赖注入
- 异步编程: 所有 I/O 操作都使用 async/await
- 安全性考虑:
- 使用恒定时间比较防止时序攻击
- SHA256 哈希验证
- 限流保护: 为回调接口添加限流策略
- 结构化日志: 使用 ILogger 记录错误
- 中介者模式: 使用 MediatR 处理复杂业务逻辑
潜在的改进建议
SaveAsAsync方法中的文件名可能会覆盖同一天的多次上传- 可以添加文件大小限制验证
- 考虑添加更详细的错误信息返回给客户端
AI 正在分析代码…
评论加载中...