using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; using Timeline.Filters; using Timeline.Helpers.Cache; using Timeline.Models; using Timeline.Models.Http; using Timeline.Models.Validation; using Timeline.Services.Imaging; using Timeline.Services.User; using Timeline.Services.User.Avatar; namespace Timeline.Controllers { /// /// Operations about user avatar. /// [ApiController] [ProducesErrorResponseType(typeof(CommonResponse))] public class UserAvatarController : Controller { private readonly IUserService _userService; private readonly IUserAvatarService _service; public UserAvatarController(IUserService userService, IUserAvatarService service) { _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; try { long userId = await _userService.GetUserIdByUsernameAsync(username); return await DataCacheHelper.GenerateActionResult(this, () => _service.GetAvatarDigestAsync(userId), () => _service.GetAvatarAsync(userId)); } catch (UserNotExistException) { return NotFound(ErrorResponse.UserCommon.NotExist()); } } /// /// 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 (!this.UserHasPermission(UserPermission.UserManagement) && User.Identity!.Name != username) { return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); } long id; try { id = await _userService.GetUserIdByUsernameAsync(username); } catch (UserNotExistException) { return BadRequest(ErrorResponse.UserCommon.NotExist()); } try { var digest = await _service.SetAvatarAsync(id, body); Response.Headers.Append("ETag", $"\"{digest.ETag}\""); return Ok(); } catch (ImageException e) { return BadRequest(e.Error switch { ImageException.ErrorReason.CantDecode => ErrorResponse.UserAvatar.BadFormat_CantDecode(), ImageException.ErrorReason.UnmatchedFormat => ErrorResponse.UserAvatar.BadFormat_UnmatchedFormat(), ImageException.ErrorReason.BadSize => ErrorResponse.UserAvatar.BadFormat_BadSize(), _ => throw new Exception() }); } } /// /// 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 (!this.UserHasPermission(UserPermission.UserManagement) && User.Identity!.Name != username) { return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid()); } long id; try { id = await _userService.GetUserIdByUsernameAsync(username); } catch (UserNotExistException) { return BadRequest(ErrorResponse.UserCommon.NotExist()); } await _service.DeleteAvatarAsync(id); return Ok(); } } }