网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
using Dpz.Core.Authenticator;
using Dpz.Core.Public.ViewModel.Request;
using Dpz.Core.Public.ViewModel.Response;
using Microsoft.Extensions.Primitives;

namespace Dpz.Core.WebApi.Controllers;

/// <summary>
/// 其他的
/// </summary>
[ApiController, Route("api/[controller]")]
public class CommunityController(
    IPictureRecordService pictureRecordService,
    IAuthenticateService authenticateService,
    IArticleService articleService,
    IAppOptionService appOptionService,
    IAppLogEntryService logEntryService,
    ILogger<CommunityController> logger,
    IHybridCachingProvider cachingProvider,
    IConfiguration configuration
) : ControllerBase
{
    /// <summary>
    /// 获取banner
    /// </summary>
    /// <returns></returns>
    [HttpGet("getBanners")]
    [ProducesResponseType<List<VmPictureRecord>>(StatusCodes.Status200OK)]
    public async Task<IActionResult> GetBanners()
    {
        var banners = await GetBannerAsync();
        return Ok(banners);
    }

    /// <summary>
    /// 身份认证
    /// </summary>
    /// <returns></returns>
    [HttpPost("{platform}/auth")]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType<AuthenticatedResponse>(StatusCodes.Status200OK)]
    public async Task<IActionResult> Auth(
        [FromServices] IUserTwoFactorService userTwoFactorService,
        TokenPlatform platform,
        [FromBody] AuthDto auth
    )
    {
        var userAgent = Request.Headers.UserAgent;
        if (StringValues.IsNullOrEmpty(userAgent) && platform == TokenPlatform.None)
        {
            return BadRequest("请使用浏览器访问!");
        }

        if (await appOptionService.IsAccountLockAsync(auth.Account))
        {
            return BadRequest("短时间内多次登录失败,账户已被锁定24小时!");
        }

        #region 双因素验证

        var (key, isBind) = await userTwoFactorService.GetKeyAsync(auth.Account);

        if (isBind)
        {
            if (string.IsNullOrEmpty(auth.PinCode))
            {
                return BadRequest("PIN码为空!");
            }

            var twoFactorAuthenticator = new TwoFactorAuthenticator();
            var keyBuffer = Encoding.UTF8.GetBytes(key);
            var keyBase32 = Base32Encoding.ToString(keyBuffer);
            var twoFactorResult = twoFactorAuthenticator.ValidateTwoFactorPIN(
                keyBase32,
                auth.PinCode,
                true
            );
            if (!twoFactorResult)
            {
                return BadRequest("PIN码验证错误!");
            }
        }

        #endregion

        var authRequest = new AuthenticateRequest
        {
            Account = auth.Account,
            Password = auth.Password,
            Platform = platform,
            IpAddress = Request.GetIpAddress(),
            UserAgent = userAgent
        };
        var result = await authenticateService.AuthenticatedAsync(authRequest);

        if (result is null)
        {
            await appOptionService.LoginFailAsync(auth.Account);
            return BadRequest("账号或密码错误!");
        }

        await appOptionService.LoginSuccessfulAsync(auth.Account);
        return Ok(result);
    }

    /// <summary>
    /// 获取 双因素绑定 Key
    /// </summary>
    /// <param name="userTwoFactorService"></param>
    /// <returns></returns>
    [Authorize]
    [HttpGet("bind-two-factor")]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType<SetupCode>(StatusCodes.Status200OK)]
    public async Task<ActionResult<SetupCode>> BindTwoFactor(
        [FromServices] IUserTwoFactorService userTwoFactorService
    )
    {
        var userInfo = User.GetIdentity();
        var (key, isBind) = await userTwoFactorService.GetKeyAsync(userInfo.Id);
        if (isBind)
            return BadRequest("已绑定双因素验证");

        var twoFactorAuthenticator = new TwoFactorAuthenticator();
        var setupInfo = twoFactorAuthenticator.GenerateSetupCode(
            issuer: "叫我阿胖",
            accountTitleNoSpaces: userInfo.Id,
            accountSecretKey: key,
            secretIsBase32: false
        );
        return Ok(setupInfo);
    }

    /// <summary>
    /// 双因素绑定
    /// </summary>
    /// <param name="userTwoFactorService"></param>
    /// <param name="model"></param>
    /// <returns></returns>
    [Authorize]
    [HttpPost("bind-two-factor")]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> HandleBindTwoFactor(
        [FromServices] IUserTwoFactorService userTwoFactorService,
        BindTwoFactorDto model
    )
    {
        var userInfo = User.GetIdentity();
        var (key, isBind) = await userTwoFactorService.GetKeyAsync(userInfo.Id);
        if (isBind)
            return BadRequest("已绑定双因素验证");

        var twoFactorAuthenticator = new TwoFactorAuthenticator();
        var keyBuffer = Encoding.UTF8.GetBytes(key);
        var keyBase32 = Base32Encoding.ToString(keyBuffer);
        var twoFactorResult = twoFactorAuthenticator.ValidateTwoFactorPIN(
            keyBase32,
            model.PinCode,
            true
        );
        if (!twoFactorResult)
        {
            return BadRequest("PIN码验证错误!");
        }

        try
        {
            await userTwoFactorService.BindAsync(userInfo.Id);
        }
        catch (Exception e)
        {
            logger.LogError(e, "双因素绑定失败!");
            return BadRequest(e.Message);
        }
        return NoContent();
    }

    /// <summary>
    /// 解除双因素绑定
    /// </summary>
    /// <param name="userTwoFactorService"></param>
    /// <param name="model"></param>
    /// <returns></returns>
    [Authorize]
    [HttpPost("unbind-two-factor")]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> UnbindTwoFactor(
        [FromServices] IUserTwoFactorService userTwoFactorService,
        [FromBody] BindTwoFactorDto model
    )
    {
        var userInfo = User.GetIdentity();
        var (key, isBind) = await userTwoFactorService.GetKeyAsync(userInfo.Id);
        if (!isBind)
            return BadRequest("未绑定双因素验证");

        var twoFactorAuthenticator = new TwoFactorAuthenticator();
        var keyBuffer = Encoding.UTF8.GetBytes(key);
        var keyBase32 = Base32Encoding.ToString(keyBuffer);
        var twoFactorResult = twoFactorAuthenticator.ValidateTwoFactorPIN(
            keyBase32,
            model.PinCode,
            true
        );
        if (!twoFactorResult)
        {
            return BadRequest("PIN码验证错误!");
        }

        await userTwoFactorService.UnbindAsync(userInfo.Id);
        return NoContent();
    }

    /// <summary>
    /// 检查是否已绑定双因素验证
    /// </summary>
    /// <param name="userTwoFactorService"></param>
    /// <returns></returns>
    [Authorize]
    [HttpGet("check-bind-two-factor")]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType<bool>(StatusCodes.Status200OK)]
    public async Task<ActionResult<bool>> CheckBindTwoFactor(
        [FromServices] IUserTwoFactorService userTwoFactorService
    )
    {
        var userInfo = User.GetIdentity();
        var (_, isBind) = await userTwoFactorService.GetKeyAsync(userInfo.Id);
        return Ok(isBind);
    }

    /// <summary>
    /// 刷新Token
    /// </summary>
    /// <returns></returns>
    [HttpPost("{platform}/refresh")]
    [ProducesResponseType<AuthenticatedResponse>(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> RefreshToken(
        TokenPlatform platform,
        [FromBody] RefreshTokenDto refresh
    )
    {
        var userAgent = Request.Headers.UserAgent;
        if (StringValues.IsNullOrEmpty(userAgent) && platform == TokenPlatform.None)
        {
            return BadRequest("请使用浏览器访问!");
        }
        var request = new RefreshTokenRequest
        {
            AccessToken = refresh.AccessToken,
            RefreshToken = refresh.RefreshToken,
            Platform = platform,
            IpAddress = Request.GetIpAddress(),
            UserAgent = userAgent
        };

        var result = await authenticateService.RefreshTokenAsync(request);
        if (result is null)
            return BadRequest("Invalid request");
        return Ok(result);
    }

    /// <summary>
    /// 获取汇总信息
    /// </summary>
    /// <returns></returns>
    [HttpGet("summary")]
    [Authorize(Policy = "System")]
    [ProducesResponseType<SummaryInformation>(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> GetSummary()
    {
        const string summaryKey = "community-summary";
        var cache = await cachingProvider.GetAsync<SummaryInformation>(summaryKey);
        if (!cache.IsNull && cache.HasValue)
            return Ok(cache.Value);

        var banner = await GetBannerAsync();

        var summary = new SummaryInformation
        {
            ArticleTotalCount = await articleService.GetTotalCountAsync(),
            Banner = banner,
            LatestArticles = await articleService.GetLatestAsync(),
            TodayArticleCount = await articleService.GetTodayCountAsync(),
        };
        if (configuration["AgileConfig:env"]?.Equals("PROD", StringComparison.OrdinalIgnoreCase) == false)
        {
            SetRandomData(summary);
        }
        else
        {
            summary.LatestLogs = await GetTop100LogsAsync();
            summary.TodayAccessNumber = await logEntryService.GetLatestAccessNumberAsync();
            summary.WeekAccessNumber = await logEntryService.GetLatestAccessNumberAsync(-7);
        }
        
        await cachingProvider.SetAsync(summaryKey, summary, TimeSpan.FromHours(3));
        return Ok(summary);
    }

    [NonAction]
    private static void SetRandomData(SummaryInformation summary)
    {
        summary.TodayAccessNumber = [];
        summary.LatestLogs = $"[{DateTime.Now:HH:mm:ss} INF] this is test log";
        var random = new Random();
        for (var i = 0; i < 7; i++)
        {
            if (i == 0)
            {
                summary.TodayAccessNumber.Add(
                    new AccessSummary
                    {
                        Date = DateTime.Now.ToString("yyyy/MM/dd"),
                        Count = random.Next(100, 100000)
                    }
                );
            }
            summary.TodayAccessNumber.Add(
                new AccessSummary
                {
                    Date = DateTime.Now.AddDays(-(i + 1)).ToString("yyyy/MM/dd"),
                    Count = random.Next(100, 100000)
                }
            );
        }
    }

    private async Task<List<VmPictureRecord>> GetBannerAsync()
    {
        var cache = await cachingProvider.GetAsync<List<VmPictureRecord>>(CacheKey.BannerKey);
        if (!cache.IsNull && cache.HasValue)
            return cache.Value;

        var banner = await pictureRecordService.GetBannerAsync();

        await cachingProvider.SetAsync(CacheKey.BannerKey, banner, TimeSpan.FromHours(12));
        return banner.ToList();
    }

    [NonAction]
    private async Task<string> GetTop100LogsAsync()
    {
        var logs = await logEntryService.GetPageAsync(pageSize: 100);
        return string.Join(
            "\n",
            logs?.Events.Select(x =>
            {
                var recordTime = Convert.ToDateTime(x.Timestamp);
                return $"[{recordTime:HH:mm:ss} {x.Level}] {x.RenderedMessage}";
            }) ?? new List<string>()
        );
    }

    /// <summary>
    /// 登入
    /// </summary>
    /// <returns></returns>
    [HttpPost("SignIn"), Obsolete]
    public async Task<IActionResult> SignIn([FromBody] SignInDto dto)
    {
        var token = await authenticateService.SignInAsync(dto.UserName, dto.Password);
        if (string.IsNullOrEmpty(token))
        {
            return BadRequest(new { singIn = false });
        }

        Response.Cookies.Append(
            Program.AuthorizeCookieName,
            token,
            new CookieOptions
            {
                HttpOnly = true,
                Secure = true,
                IsEssential = true,
                Expires = DateTime.Now.AddDays(7)
            }
        );
        Response.Cookies.Append(
            Program.AccountCookieName,
            dto.UserName,
            new CookieOptions
            {
                HttpOnly = true,
                Secure = true,
                IsEssential = true,
                Expires = DateTime.Now.AddDays(7)
            }
        );
        return Ok(new { signIn = true });
    }

    /// <summary>
    /// 获取壁纸
    /// </summary>
    /// <param name="bingWallpaper"></param>
    /// <returns></returns>
    [HttpGet("wallpaper")]
    public async Task<ActionResult<List<Wallpaper>>> Wallpaper(
        [FromServices] IBingWallpaper bingWallpaper
    )
    {
        var wallpapers = await bingWallpaper.GetTodayWallpapersAsync();
        return Ok(wallpapers);
    }

    /// <summary>
    /// 获取页脚内容
    /// </summary>
    /// <returns></returns>
    [HttpGet("footer")]
    [ProducesResponseType<string>(StatusCodes.Status200OK)]
    public async Task<IActionResult> GetFooter()
    {
        var content = await appOptionService.GetFooterContentAsync();
        return Ok(content);
    }

    /// <summary>
    /// 保存页脚内容
    /// </summary>
    /// <param name="footer"></param>
    /// <returns></returns>
    [HttpPost("footer"), Authorize(Policy = "System")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> SaveFooter([FromBody] SaveFooterDto footer)
    {
        await appOptionService.SaveFooterContentAsync(footer.Content);
        return NoContent();
    }

#if DEBUG
    private static HubConnection _connection;

    /// <summary>
    ///
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    [HttpPost("send")]
    public async Task<IActionResult> SendMessage([FromBody] string message)
    {
        if (_connection == null || _connection.State == HubConnectionState.Disconnected)
        {
            _connection = new HubConnectionBuilder()
                .WithUrl("https://localhost:37701/notification")
                .WithAutomaticReconnect()
                .Build();
            await _connection.StartAsync();
        }

        await _connection.InvokeCoreAsync("SendSystemNotification", new object[] { message });

        return NoContent();
    }
#endif
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

上述代码是一个 ASP.NET Core Web API 控制器,名为 CommunityController,它提供了一系列与社区相关的功能。以下是对代码中各个部分的详细解释:

1. 控制器概述

  • 控制器名称: CommunityController
  • 路由: api/community
  • 功能: 该控制器处理与社区相关的请求,包括身份认证、双因素验证、获取和管理内容(如横幅、文章、页脚等)。

2. 依赖注入

控制器通过构造函数注入了多个服务:

  • IPictureRecordService: 用于处理图片记录(如横幅)。
  • IAuthenticateService: 处理用户身份认证。
  • IArticleService: 处理文章相关的操作。
  • IAppOptionService: 处理应用程序选项(如页脚内容)。
  • IAppLogEntryService: 处理日志条目。
  • ILogger<CommunityController>: 用于记录日志。
  • IHybridCachingProvider: 用于缓存操作。
  • IConfiguration: 用于访问应用程序配置。

3. 主要功能

以下是控制器中定义的主要 API 端点及其功能:

获取横幅

  • 方法: GetBanners
  • HTTP 方法: GET
  • 功能: 获取横幅图片记录并返回。

身份认证

  • 方法: Auth
  • HTTP 方法: POST
  • 功能: 处理用户登录请求,支持双因素验证。

双因素验证

  • 方法: BindTwoFactorHandleBindTwoFactor
  • HTTP 方法: GET 和 POST
  • 功能: 绑定和解除双因素验证,检查用户是否已绑定双因素验证。

刷新 Token

  • 方法: RefreshToken
  • HTTP 方法: POST
  • 功能: 刷新用户的身份验证 Token。

获取汇总信息

  • 方法: GetSummary
  • HTTP 方法: GET
  • 功能: 获取社区的汇总信息,包括文章总数、最新文章、访问统计等。

获取和保存页脚内容

  • 方法: GetFooterSaveFooter
  • HTTP 方法: GET 和 POST
  • 功能: 获取和保存页脚内容。

获取壁纸

  • 方法: Wallpaper
  • HTTP 方法: GET
  • 功能: 获取当天的壁纸。

发送消息(仅在调试模式下)

  • 方法: SendMessage
  • HTTP 方法: POST
  • 功能: 发送系统通知消息。

4. 其他功能

  • 缓存: 使用 IHybridCachingProvider 进行数据缓存,以提高性能。
  • 双因素验证: 使用 TwoFactorAuthenticator 进行双因素身份验证。
  • 日志记录: 使用 ILogger 记录操作和错误信息。

5. 代码结构

  • 注释: 代码中包含了详细的 XML 注释,描述了每个方法的功能和返回值。
  • 错误处理: 通过返回适当的 HTTP 状态码(如 400、401、204 等)来处理错误情况。

总结

CommunityController 提供了一系列与社区相关的功能,主要集中在用户身份认证、双因素验证、内容管理和统计信息等方面。通过依赖注入和 ASP.NET Core 的特性,控制器能够高效地处理请求并返回相应的结果。

loading