网站首页 网站源码
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
}
上述代码是一个 ASP.NET Core Web API 控制器,名为 CommunityController
,它提供了一系列与社区相关的功能。以下是对代码中各个部分的详细解释:
CommunityController
api/community
控制器通过构造函数注入了多个服务:
IPictureRecordService
: 用于处理图片记录(如横幅)。IAuthenticateService
: 处理用户身份认证。IArticleService
: 处理文章相关的操作。IAppOptionService
: 处理应用程序选项(如页脚内容)。IAppLogEntryService
: 处理日志条目。ILogger<CommunityController>
: 用于记录日志。IHybridCachingProvider
: 用于缓存操作。IConfiguration
: 用于访问应用程序配置。以下是控制器中定义的主要 API 端点及其功能:
GetBanners
Auth
BindTwoFactor
和 HandleBindTwoFactor
RefreshToken
GetSummary
GetFooter
和 SaveFooter
Wallpaper
SendMessage
IHybridCachingProvider
进行数据缓存,以提高性能。TwoFactorAuthenticator
进行双因素身份验证。ILogger
记录操作和错误信息。CommunityController
提供了一系列与社区相关的功能,主要集中在用户身份认证、双因素验证、内容管理和统计信息等方面。通过依赖注入和 ASP.NET Core 的特性,控制器能够高效地处理请求并返回相应的结果。