From 05ccb4d8f1bbe3fb64e117136b4a89bcfb0b0b33 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 27 Oct 2020 19:21:35 +0800 Subject: Split front and back end. --- .../Timeline/Controllers/UserAvatarController.cs | 174 +++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 BackEnd/Timeline/Controllers/UserAvatarController.cs (limited to 'BackEnd/Timeline/Controllers/UserAvatarController.cs') diff --git a/BackEnd/Timeline/Controllers/UserAvatarController.cs b/BackEnd/Timeline/Controllers/UserAvatarController.cs new file mode 100644 index 00000000..bc4afa30 --- /dev/null +++ b/BackEnd/Timeline/Controllers/UserAvatarController.cs @@ -0,0 +1,174 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; +using System; +using System.Threading.Tasks; +using Timeline.Auth; +using Timeline.Filters; +using Timeline.Helpers; +using Timeline.Models; +using Timeline.Models.Http; +using Timeline.Models.Validation; +using Timeline.Services; +using Timeline.Services.Exceptions; +using static Timeline.Resources.Controllers.UserAvatarController; + +namespace Timeline.Controllers +{ + /// + /// Operations about user avatar. + /// + [ApiController] + [ProducesErrorResponseType(typeof(CommonResponse))] + public class UserAvatarController : Controller + { + private readonly ILogger _logger; + + private readonly IUserService _userService; + private readonly IUserAvatarService _service; + + /// + /// + /// + public UserAvatarController(ILogger logger, IUserService userService, IUserAvatarService service) + { + _logger = logger; + _userService = userService; + _service = service; + } + + /// + /// Get avatar of a user. + /// + /// Username of the user to get avatar of. + /// If-None-Match header. + /// Avatar data. + [HttpGet("users/{username}/avatar")] + [Produces("image/png", "image/jpeg", "image/gif", "image/webp", "application/json", "text/json")] + [ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Get([FromRoute][Username] string username, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch) + { + _ = ifNoneMatch; + long id; + try + { + id = await _userService.GetUserIdByUsername(username); + } + catch (UserNotExistException e) + { + _logger.LogInformation(e, Log.Format(LogGetUserNotExist, ("Username", username))); + return NotFound(ErrorResponse.UserCommon.NotExist()); + } + + return await DataCacheHelper.GenerateActionResult(this, () => _service.GetAvatarETag(id), async () => + { + var avatar = await _service.GetAvatar(id); + return avatar.ToCacheableData(); + }); + } + + /// + /// Set avatar of a user. You have to be administrator to change other's. + /// + /// Username of the user to set avatar of. + /// The avatar data. + [HttpPut("users/{username}/avatar")] + [Authorize] + [Consumes("image/png", "image/jpeg", "image/gif", "image/webp")] + [MaxContentLength(1000 * 1000 * 10)] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task Put([FromRoute][Username] string username, [FromBody] ByteData body) + { + if (!User.IsAdministrator() && User.Identity.Name != username) + { + _logger.LogInformation(Log.Format(LogPutForbid, + ("Operator Username", User.Identity.Name), ("Username To Put Avatar", username))); + return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); + } + + long id; + try + { + id = await _userService.GetUserIdByUsername(username); + } + catch (UserNotExistException e) + { + _logger.LogInformation(e, Log.Format(LogPutUserNotExist, ("Username", username))); + return BadRequest(ErrorResponse.UserCommon.NotExist()); + } + + try + { + var etag = await _service.SetAvatar(id, new Avatar + { + Data = body.Data, + Type = body.ContentType + }); + + _logger.LogInformation(Log.Format(LogPutSuccess, + ("Username", username), ("Mime Type", Request.ContentType))); + + Response.Headers.Append("ETag", new EntityTagHeaderValue($"\"{etag}\"").ToString()); + + return Ok(); + } + catch (ImageException e) + { + _logger.LogInformation(e, Log.Format(LogPutUserBadFormat, ("Username", username))); + return BadRequest(e.Error switch + { + ImageException.ErrorReason.CantDecode => ErrorResponse.UserAvatar.BadFormat_CantDecode(), + ImageException.ErrorReason.UnmatchedFormat => ErrorResponse.UserAvatar.BadFormat_UnmatchedFormat(), + ImageException.ErrorReason.NotSquare => ErrorResponse.UserAvatar.BadFormat_BadSize(), + _ => + throw new Exception(ExceptionUnknownAvatarFormatError) + }); + } + } + + /// + /// Reset the avatar to the default one. You have to be administrator to reset other's. + /// + /// Username of the user. + /// Succeeded to reset. + /// Error code is 10010001 if user does not exist. + /// You have not logged in. + /// You are not administrator. + [HttpDelete("users/{username}/avatar")] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [Authorize] + public async Task Delete([FromRoute][Username] string username) + { + if (!User.IsAdministrator() && User.Identity.Name != username) + { + _logger.LogInformation(Log.Format(LogDeleteForbid, + ("Operator Username", User.Identity.Name), ("Username To Delete Avatar", username))); + return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); + } + + long id; + try + { + id = await _userService.GetUserIdByUsername(username); + } + catch (UserNotExistException e) + { + _logger.LogInformation(e, Log.Format(LogDeleteNotExist, ("Username", username))); + return BadRequest(ErrorResponse.UserCommon.NotExist()); + } + + await _service.SetAvatar(id, null); + return Ok(); + } + } +} -- cgit v1.2.3