diff options
Diffstat (limited to 'Timeline/Controllers')
-rw-r--r-- | Timeline/Controllers/TimelineController.cs | 114 | ||||
-rw-r--r-- | Timeline/Controllers/TokenController.cs | 18 | ||||
-rw-r--r-- | Timeline/Controllers/UserAvatarController.cs | 64 | ||||
-rw-r--r-- | Timeline/Controllers/UserController.cs | 50 |
4 files changed, 226 insertions, 20 deletions
diff --git a/Timeline/Controllers/TimelineController.cs b/Timeline/Controllers/TimelineController.cs index 72404ea3..43178ac6 100644 --- a/Timeline/Controllers/TimelineController.cs +++ b/Timeline/Controllers/TimelineController.cs @@ -17,8 +17,12 @@ using Timeline.Services.Exceptions; namespace Timeline.Controllers
{
+ /// <summary>
+ /// Operations about timeline.
+ /// </summary>
[ApiController]
[CatchTimelineNotExistException]
+ [ProducesErrorResponseType(typeof(CommonResponse))]
public class TimelineController : Controller
{
private readonly ILogger<TimelineController> _logger;
@@ -28,6 +32,9 @@ namespace Timeline.Controllers private readonly IMapper _mapper;
+ /// <summary>
+ ///
+ /// </summary>
public TimelineController(ILogger<TimelineController> logger, IUserService userService, ITimelineService service, IMapper mapper)
{
_logger = logger;
@@ -36,7 +43,16 @@ namespace Timeline.Controllers _mapper = mapper;
}
+ /// <summary>
+ /// List all timelines.
+ /// </summary>
+ /// <param name="relate">A username. If set, only timelines related to the user will return.</param>
+ /// <param name="relateType">Specify the relation type, may be 'own' or 'join'. If not set, both type will return.</param>
+ /// <param name="visibility">"Private" or "Register" or "Public". If set, only timelines whose visibility is specified one will return.</param>
+ /// <returns>The timeline list.</returns>
[HttpGet("timelines")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<List<TimelineInfo>>> TimelineList([FromQuery][Username] string? relate, [FromQuery][RegularExpression("(own)|(join)")] string? relateType, [FromQuery] string? visibility)
{
List<TimelineVisibility>? visibilityFilter = null;
@@ -93,7 +109,18 @@ namespace Timeline.Controllers return result;
}
+ /// <summary>
+ /// Get info of a timeline.
+ /// </summary>
+ /// <param name="name">The timeline name.</param>
+ /// <param name="checkUniqueId">A unique id. If specified and if-modified-since is also specified, the timeline info will return when unique id is not the specified one even if it is not modified.</param>
+ /// <param name="queryIfModifiedSince">Same effect as If-Modified-Since header and take precedence than it.</param>
+ /// <param name="headerIfModifiedSince">If specified, will return 304 if not modified.</param>
+ /// <returns>The timeline info.</returns>
[HttpGet("timelines/{name}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status304NotModified)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<TimelineInfo>> TimelineGet([FromRoute][GeneralTimelineName] string name, [FromQuery] string? checkUniqueId, [FromQuery(Name = "ifModifiedSince")] DateTime? queryIfModifiedSince, [FromHeader(Name = "If-Modified-Since")] DateTime? headerIfModifiedSince)
{
DateTime? ifModifiedSince = null;
@@ -140,7 +167,17 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Get posts of a timeline.
+ /// </summary>
+ /// <param name="name">The name of the timeline.</param>
+ /// <param name="modifiedSince">If set, only posts modified since the time will return.</param>
+ /// <param name="includeDeleted">If set to true, deleted post will also return.</param>
+ /// <returns>The post list.</returns>
[HttpGet("timelines/{name}/posts")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<List<TimelinePostInfo>>> PostListGet([FromRoute][GeneralTimelineName] string name, [FromQuery] DateTime? modifiedSince, [FromQuery] bool? includeDeleted)
{
if (!this.IsAdministrator() && !await _service.HasReadPermission(name, this.GetOptionalUserId()))
@@ -154,9 +191,22 @@ namespace Timeline.Controllers return result;
}
+ /// <summary>
+ /// Get the data of a post. Usually a image post.
+ /// </summary>
+ /// <param name="name">Timeline name.</param>
+ /// <param name="id">The id of the post.</param>
+ /// <param name="ifNoneMatch">If-None-Match header.</param>
+ /// <returns>The data.</returns>
[HttpGet("timelines/{name}/posts/{id}/data")]
- public async Task<ActionResult<List<TimelinePostInfo>>> PostDataGet([FromRoute][GeneralTimelineName] string name, [FromRoute] long id)
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task<IActionResult> PostDataGet([FromRoute][GeneralTimelineName] string name, [FromRoute] long id, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch)
{
+ _ = ifNoneMatch;
if (!this.IsAdministrator() && !await _service.HasReadPermission(name, this.GetOptionalUserId()))
{
return StatusCode(StatusCodes.Status403Forbidden, ErrorResponse.Common.Forbid());
@@ -180,8 +230,18 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Create a new post.
+ /// </summary>
+ /// <param name="name">Timeline name.</param>
+ /// <param name="body"></param>
+ /// <returns>Info of new post.</returns>
[HttpPost("timelines/{name}/posts")]
[Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<TimelinePostInfo>> PostPost([FromRoute][GeneralTimelineName] string name, [FromBody] TimelinePostCreateRequest body)
{
var id = this.GetUserId();
@@ -238,8 +298,17 @@ namespace Timeline.Controllers return result;
}
+ /// <summary>
+ /// Delete a post.
+ /// </summary>
+ /// <param name="name">Timeline name.</param>
+ /// <param name="id">Post id.</param>
+ /// <returns>Info of deletion.</returns>
[HttpDelete("timelines/{name}/posts/{id}")]
[Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<CommonDeleteResponse>> PostDelete([FromRoute][GeneralTimelineName] string name, [FromRoute] long id)
{
if (!this.IsAdministrator() && !await _service.HasPostModifyPermission(name, id, this.GetUserId()))
@@ -257,8 +326,17 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Change properties of a timeline.
+ /// </summary>
+ /// <param name="name">Timeline name.</param>
+ /// <param name="body"></param>
+ /// <returns>The new info.</returns>
[HttpPatch("timelines/{name}")]
[Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<TimelineInfo>> TimelinePatch([FromRoute][GeneralTimelineName] string name, [FromBody] TimelinePatchRequest body)
{
if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
@@ -271,8 +349,17 @@ namespace Timeline.Controllers return result;
}
+ /// <summary>
+ /// Add a member to timeline.
+ /// </summary>
+ /// <param name="name">Timeline name.</param>
+ /// <param name="member">The new member's username.</param>
[HttpPut("timelines/{name}/members/{member}")]
[Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult> TimelineMemberPut([FromRoute][GeneralTimelineName] string name, [FromRoute][Username] string member)
{
if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
@@ -291,8 +378,16 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Remove a member from timeline.
+ /// </summary>
+ /// <param name="name">Timeline name.</param>
+ /// <param name="member">The member's username.</param>
[HttpDelete("timelines/{name}/members/{member}")]
[Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult> TimelineMemberDelete([FromRoute][GeneralTimelineName] string name, [FromRoute][Username] string member)
{
if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
@@ -311,8 +406,16 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Create a timeline.
+ /// </summary>
+ /// <param name="body"></param>
+ /// <returns>Info of new timeline.</returns>
[HttpPost("timelines")]
[Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult<TimelineInfo>> TimelineCreate([FromBody] TimelineCreateRequest body)
{
var userId = this.GetUserId();
@@ -329,8 +432,17 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Delete a timeline.
+ /// </summary>
+ /// <param name="name">Timeline name.</param>
+ /// <returns>Info of deletion.</returns>
[HttpDelete("timelines/{name}")]
[Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<CommonDeleteResponse>> TimelineDelete([FromRoute][TimelineName] string name)
{
if (!this.IsAdministrator() && !(await _service.HasManagePermission(name, this.GetUserId())))
diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs index cd67225c..8f2ca600 100644 --- a/Timeline/Controllers/TokenController.cs +++ b/Timeline/Controllers/TokenController.cs @@ -1,5 +1,6 @@ using AutoMapper;
using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
@@ -13,8 +14,12 @@ using static Timeline.Resources.Controllers.TokenController; namespace Timeline.Controllers
{
+ /// <summary>
+ /// Operation about tokens.
+ /// </summary>
[Route("token")]
[ApiController]
+ [ProducesErrorResponseType(typeof(CommonResponse))]
public class TokenController : Controller
{
private readonly IUserTokenManager _userTokenManager;
@@ -23,6 +28,7 @@ namespace Timeline.Controllers private readonly IMapper _mapper;
+ /// <summary></summary>
public TokenController(IUserTokenManager userTokenManager, ILogger<TokenController> logger, IClock clock, IMapper mapper)
{
_userTokenManager = userTokenManager;
@@ -31,8 +37,14 @@ namespace Timeline.Controllers _mapper = mapper;
}
+ /// <summary>
+ /// Create a new token for a user.
+ /// </summary>
+ /// <returns>Result of token creation.</returns>
[HttpPost("create")]
[AllowAnonymous]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<CreateTokenResponse>> Create([FromBody] CreateTokenRequest request)
{
void LogFailure(string reason, Exception? e = null)
@@ -75,8 +87,14 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Verify a token.
+ /// </summary>
+ /// <returns>Result of token verification.</returns>
[HttpPost("verify")]
[AllowAnonymous]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<VerifyTokenResponse>> Verify([FromBody] VerifyTokenRequest request)
{
void LogFailure(string reason, Exception? e = null, params (string, object?)[] otherProperties)
diff --git a/Timeline/Controllers/UserAvatarController.cs b/Timeline/Controllers/UserAvatarController.cs index b2e2e852..32f63fc6 100644 --- a/Timeline/Controllers/UserAvatarController.cs +++ b/Timeline/Controllers/UserAvatarController.cs @@ -3,10 +3,12 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
+using System.IO;
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;
@@ -15,7 +17,11 @@ using static Timeline.Resources.Controllers.UserAvatarController; namespace Timeline.Controllers
{
+ /// <summary>
+ /// Operations about user avatar.
+ /// </summary>
[ApiController]
+ [ProducesErrorResponseType(typeof(CommonResponse))]
public class UserAvatarController : Controller
{
private readonly ILogger<UserAvatarController> _logger;
@@ -23,6 +29,9 @@ namespace Timeline.Controllers private readonly IUserService _userService;
private readonly IUserAvatarService _service;
+ /// <summary>
+ ///
+ /// </summary>
public UserAvatarController(ILogger<UserAvatarController> logger, IUserService userService, IUserAvatarService service)
{
_logger = logger;
@@ -30,9 +39,19 @@ namespace Timeline.Controllers _service = service;
}
+ /// <summary>
+ /// Get avatar of a user.
+ /// </summary>
+ /// <param name="username">Username of the user to get avatar of.</param>
+ /// <param name="ifNoneMatch">If-None-Match header.</param>
+ /// <returns>Avatar data.</returns>
[HttpGet("users/{username}/avatar")]
- public async Task<IActionResult> Get([FromRoute][Username] string username)
+ [ProducesResponseType(typeof(byte[]), StatusCodes.Status200OK)]
+ [ProducesResponseType(typeof(void), StatusCodes.Status304NotModified)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task<IActionResult> Get([FromRoute][Username] string username, [FromHeader(Name = "If-None-Match")] string? ifNoneMatch)
{
+ _ = ifNoneMatch;
long id;
try
{
@@ -51,16 +70,21 @@ namespace Timeline.Controllers });
}
+ /// <summary>
+ /// Set avatar of a user. You have to be administrator to change other's.
+ /// </summary>
+ /// <param name="username">Username of the user to set avatar of.</param>
+ /// <param name="body">The avatar data.</param>
[HttpPut("users/{username}/avatar")]
[Authorize]
- [RequireContentType, RequireContentLength]
[Consumes("image/png", "image/jpeg", "image/gif", "image/webp")]
- public async Task<IActionResult> Put([FromRoute][Username] string username)
+ [MaxContentLength(1000 * 1000 * 10)]
+ [ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ public async Task<IActionResult> Put([FromRoute][Username] string username, [FromBody] ByteData body)
{
- var contentLength = Request.ContentLength!.Value;
- if (contentLength > 1000 * 1000 * 10)
- return BadRequest(ErrorResponse.Common.Content.TooBig("10MB"));
-
if (!User.IsAdministrator() && User.Identity.Name != username)
{
_logger.LogInformation(Log.Format(LogPutForbid,
@@ -81,20 +105,10 @@ namespace Timeline.Controllers try
{
- var data = new byte[contentLength];
- var bytesRead = await Request.Body.ReadAsync(data);
-
- if (bytesRead != contentLength)
- return BadRequest(ErrorResponse.Common.Content.UnmatchedLength_Smaller());
-
- var extraByte = new byte[1];
- if (await Request.Body.ReadAsync(extraByte) != 0)
- return BadRequest(ErrorResponse.Common.Content.UnmatchedLength_Bigger());
-
await _service.SetAvatar(id, new Avatar
{
- Data = data,
- Type = Request.ContentType
+ Data = body.Data,
+ Type = body.ContentType
});
_logger.LogInformation(Log.Format(LogPutSuccess,
@@ -115,7 +129,19 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Reset the avatar to the default one. You have to be administrator to reset other's.
+ /// </summary>
+ /// <param name="username">Username of the user.</param>
+ /// <response code="200">Succeeded to reset.</response>
+ /// <response code="400">Error code is 10010001 if user does not exist.</response>
+ /// <response code="401">You have not logged in.</response>
+ /// <response code="403">You are not administrator.</response>
[HttpDelete("users/{username}/avatar")]
+ [ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
[Authorize]
public async Task<IActionResult> Delete([FromRoute][Username] string username)
{
diff --git a/Timeline/Controllers/UserController.cs b/Timeline/Controllers/UserController.cs index 3986bb5b..02c09aab 100644 --- a/Timeline/Controllers/UserController.cs +++ b/Timeline/Controllers/UserController.cs @@ -17,7 +17,11 @@ using static Timeline.Resources.Messages; namespace Timeline.Controllers
{
+ /// <summary>
+ /// Operations about users.
+ /// </summary>
[ApiController]
+ [ProducesErrorResponseType(typeof(CommonResponse))]
public class UserController : Controller
{
private readonly ILogger<UserController> _logger;
@@ -25,6 +29,7 @@ namespace Timeline.Controllers private readonly IUserDeleteService _userDeleteService;
private readonly IMapper _mapper;
+ /// <summary></summary>
public UserController(ILogger<UserController> logger, IUserService userService, IUserDeleteService userDeleteService, IMapper mapper)
{
_logger = logger;
@@ -35,7 +40,12 @@ namespace Timeline.Controllers private UserInfo ConvertToUserInfo(User user) => _mapper.Map<UserInfo>(user);
+ /// <summary>
+ /// Get all users.
+ /// </summary>
+ /// <returns>All user list.</returns>
[HttpGet("users")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<UserInfo[]>> List()
{
var users = await _userService.GetUsers();
@@ -43,7 +53,14 @@ namespace Timeline.Controllers return Ok(result);
}
+ /// <summary>
+ /// Get a user's info.
+ /// </summary>
+ /// <param name="username">Username of the user.</param>
+ /// <returns>User info.</returns>
[HttpGet("users/{username}")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserInfo>> Get([FromRoute][Username] string username)
{
try
@@ -58,7 +75,18 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Change a user's property.
+ /// </summary>
+ /// <param name="body"></param>
+ /// <param name="username">Username of the user to change.</param>
+ /// <returns>The new user info.</returns>
[HttpPatch("users/{username}"), Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserInfo>> Patch([FromBody] UserPatchRequest body, [FromRoute][Username] string username)
{
if (this.IsAdministrator())
@@ -101,7 +129,15 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Delete a user and all his related data. You have to be administrator.
+ /// </summary>
+ /// <param name="username">Username of the user to delete.</param>
+ /// <returns>Info of deletion.</returns>
[HttpDelete("users/{username}"), AdminAuthorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<CommonDeleteResponse>> Delete([FromRoute][Username] string username)
{
var delete = await _userDeleteService.DeleteUser(username);
@@ -111,7 +147,15 @@ namespace Timeline.Controllers return Ok(CommonDeleteResponse.NotExist());
}
+ /// <summary>
+ /// Create a new user. You have to be administrator.
+ /// </summary>
+ /// <returns>The new user's info.</returns>
[HttpPost("userop/createuser"), AdminAuthorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<UserInfo>> CreateUser([FromBody] CreateUserRequest body)
{
try
@@ -125,7 +169,13 @@ namespace Timeline.Controllers }
}
+ /// <summary>
+ /// Change password with old password.
+ /// </summary>
[HttpPost("userop/changepassword"), Authorize]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult> ChangePassword([FromBody] ChangePasswordRequest request)
{
try
|