using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; 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 : MyControllerBase { 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; long userId = await _userService.GetUserIdByUsernameAsync(username); return await DataCacheHelper.GenerateActionResult(this, () => _service.GetAvatarDigestAsync(userId), () => _service.GetAvatarAsync(userId)); } /// /// 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 (!UserHasPermission(UserPermission.UserManagement) && User.Identity!.Name != username) { return ForbidWithCommonResponse(Resource.MessageForbidNotAdministratorOrOwner); } long id = await _userService.GetUserIdByUsernameAsync(username); 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 => new CommonResponse(ErrorCodes.Image.CantDecode, Resource.MessageImageDecodeFailed), ImageException.ErrorReason.UnmatchedFormat => new CommonResponse(ErrorCodes.Image.UnmatchedFormat, Resource.MessageImageFormatUnmatch), ImageException.ErrorReason.BadSize => new CommonResponse(ErrorCodes.Image.BadSize, Resource.MessageImageBadSize), _ => new CommonResponse(ErrorCodes.Image.Unknown, Resource.MessageImageUnknownError) }); } } /// /// 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 (!UserHasPermission(UserPermission.UserManagement) && User.Identity!.Name != username) { return ForbidWithCommonResponse(Resource.MessageForbidNotAdministratorOrOwner); } long id = await _userService.GetUserIdByUsernameAsync(username); await _service.DeleteAvatarAsync(id); return Ok(); } } }