using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Modules.User.Application; using Modules.User.Application.Commands; using Modules.User.Application.Queries; using Modules.User.WebApi.Models; using System.Net; using Modules.User.Api.Models; using Modules.User.Application.Commands.User; using Modules.User.Application.Commands.User.Access; using Modules.User.Application.Commands.User.Ban; using Modules.User.Application.Commands.User.PersonalData; using Modules.User.Application.Commands.User.Session; using Modules.User.Application.Queries.User; using Modules.User.WebApi.Models.User.Access; namespace Modules.User.WebApi.Controllers; [ApiController] [Route("[controller]")] [ProducesResponseType(400, StatusCode = 400, Type = typeof(ProblemDetails))] [ProducesResponseType(401, StatusCode = 401, Type = typeof(UnauthorizedResult))] [Authorize] public class UserController : ControllerBase { private readonly IMediator _mediator; private readonly ILogger _logger; public UserController(IMediator mediator, ILogger logger) { _mediator = mediator; _logger = logger; } [HttpPost("Login")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(AuthenticationResultModel))] [AllowAnonymous] public async Task Login(LoginModel model) { var loginResult = await _mediator.Send(new LoginCommand { Email = model.Login, Password = model.Password, IsAdmin = false, Ip = model.Ip, Response = Response, //CookiePath = "Account/Refresh", CookiePath = "/", }); return Ok(new AuthenticationResultModel { AccessToken = loginResult.AccessToken, SessionExpireDate = loginResult.SessionExpireDate.ToString("O"), }); } [HttpPost("Refresh")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(AuthenticationResultModel))] [AllowAnonymous] public async Task Refresh(RefreshModel model) { var refreshResult = await _mediator.Send(new RefreshTokensCommand { Ip = model.Ip, Response = Response, //CookiePath = "Account/Refresh", CookiePath = "/", }); //if (tokens != null) //{ // return Ok(tokens.Value.AccessToken); //} //else //{ // return Unauthorized(); //} return refreshResult == null ? Unauthorized() : Ok(new AuthenticationResultModel { AccessToken = refreshResult.AccessToken, SessionExpireDate = refreshResult.SessionExpireDate.ToString("O"), }); //return tokens == null ? Unauthorized() : Ok(new TokensModel //{ // AccessToken = tokens.Value.AccessToken, // RefreshToken = tokens.Value.RefreshToken, //}); } [HttpPost("Register")] [AllowAnonymous] public async Task Register(RegisterModel model) { var token = await _mediator.Send(new CreateUserCommand { Nickname = model.Nickname, Email = model.Login, Password = model.Password, //IsAdmin = false, }); } #region Sessions [HttpGet("sessions")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(List))] public async Task GetSessions() { var sessions = await _mediator.Send(new GetAccountSessionsQuery()); return Ok(sessions.Select(q => new Session { Id = q.Id, IsCurrentSession = q.IsCurrentSession, Country = q.ClientInfo.Location.Country, Region = q.ClientInfo.Location.Region, OnlineStatus = q.OnlineStatus switch { Application.Models.UserOnlineStatus.Online => UserOnlineStatus.Online, Application.Models.UserOnlineStatus.Away => UserOnlineStatus.Away, Application.Models.UserOnlineStatus.Offline => UserOnlineStatus.Offline, _ => UserOnlineStatus.Unknown, }, LastOnline = q.LastOnline, //Latitude = q.ClientInfo.Location.Latitude, //Longitude = q.ClientInfo.Location.Longutude, UserAgent = q.ClientInfo.UserAgent, ExpiredDate = q.ExpiredDate, })); } [HttpDelete("sessions/{id:guid}")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public async Task DeleteSession(Guid id) { try { await _mediator.Send(new DeleteSessionCommand { SessionId = id, }); return Ok(); } catch (UnauthorizedAccessException ex) { //TODO: log return Unauthorized(); } } [HttpDelete("sessions/current")] public async Task DeleteCurrentSession() { try { await _mediator.Send(new DeleteCurrentSessionCommand()); return Ok(); } catch (UnauthorizedAccessException ex) { //TODO: log return Unauthorized(); } } [HttpDelete("sessions/all")] public async Task DeleteAllSessions() { try { await _mediator.Send(new DeleteAllSessionsCommand()); return Ok(); } catch (UnauthorizedAccessException ex) { //TODO: log return Unauthorized(); } } #endregion #region Access [HttpPost("ban")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] public async Task BanUser(AccountBanModel model, CancellationToken cancellationToken) { await _mediator.Send(new BanAccountCommand { AccountId = model.AccountId, Reason = model.Reason, ExpiresAtUtc = model.ExpiresAtUtc, }, cancellationToken); return Ok(); } [HttpPost("unban")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] public async Task UnbanUser(AccountUnbanModel model, CancellationToken cancellationToken) { await _mediator.Send(new UnbanAccountCommand() { AccountId = model.AccountId, Reason = model.Reason, }, cancellationToken); return Ok(); } [HttpPost("{id:guid}/role/assign")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] public async Task AssignRoleToUser(Guid id, RoleAssignModel model, CancellationToken cancellationToken) { await _mediator.Send(new RoleAssignToUserCommand { AccountId = id, RoleId = model.RoleId, AssignReason = model.AssignReason, }, cancellationToken); return Ok(); } [HttpPost("{id:guid}/role/revoke")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] public async Task RevokeRoleFromUser(Guid id, RoleRevokeModel model, CancellationToken cancellationToken) { await _mediator.Send(new RoleRevokeFromUserCommand() { AccountId = id, RoleId = model.RoleId, RevokeReason = model.RevokeReason, }, cancellationToken); return Ok(); } [HttpPost("{id:guid}/permission/assign")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] public async Task GrantPermissionToUser(Guid id, PermissionGrantModel model, CancellationToken cancellationToken) { await _mediator.Send(new PermissionGrantToUserCommand() { AccountId = id, PermissionId = model.PermissionId, GrantReason = model.GrantReason, }, cancellationToken); return Ok(); } [HttpPost("{id:guid}/permission/revoke")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] public async Task RevokePermissionFromUser(Guid id, PermissionRevokeModel model, CancellationToken cancellationToken) { await _mediator.Send(new PermissionRevokeFromUserCommand() { AccountId = id, PermissionId = model.PermissionId, RevokeReason = model.RevokeReason, }, cancellationToken); return Ok(); } #endregion [HttpGet("list")] public async Task> GetUsers(bool? hasAvatar, string? nickName, DateTimeOffset? lastOnlineFrom = null, DateTimeOffset? lastOnlineTo = null, bool? isBanned = null, int page = 1, int itemsOnPage = 10) { var usersPaged = await _mediator.Send(new GetUserListQuery() { HasAvatar = hasAvatar, NickName = nickName, LastOnlineFrom = lastOnlineFrom, LastOnlineTo = lastOnlineTo, IsBanned = isBanned, ItemsOnPage = itemsOnPage, Page = page, }); return new PagedData { PagesCount = usersPaged.PagesCount, Page = usersPaged.Page, ItemsOnPage = usersPaged.ItemsOnPage, ItemsCount = usersPaged.ItemsCount, Items = usersPaged.Items.Select(q => new UserInfoShort { Id = q.Id, NickName = q.NickName, FirstName = q.FirstName, Patronymic = q.Patronymic, LastName = q.LastName, Email = q.Email, HasAvatar = q.HasAvatar, BirthDate = q.BirthDate, LanguageId = q.LanguageId, OnlineStatus = q.OnlineStatus switch { Application.Models.UserOnlineStatus.Online => UserOnlineStatus.Online, Application.Models.UserOnlineStatus.Away => UserOnlineStatus.Away, Application.Models.UserOnlineStatus.Offline => UserOnlineStatus.Offline, _ => UserOnlineStatus.Unknown, }, LastOnline = q.LastOnline, BanStatus = new BanStatus(q.BanStatus.IsBanned, q.BanStatus.BannedUntil), // Roles = q.roles }) }; } // [HttpGet("UserAvatar")] // [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))] // [AllowAnonymous] // public async Task GetUserAvatar(string userId) // { // if (!string.IsNullOrEmpty(objectId)) // { // var media = await _mediator.Send(new GetUserAvatarQuery // { // ObjectId = objectId, // }); // if (media == null) // { // return NotFound(); // } // else // { // return new FileStreamResult(media.Data, media.ContentType); // } // } // else // { // return NotFound(); // } // } // [HttpGet] // [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(UserInfo))] // public async Task GetUser() // { // var user = await _userContext.GetUserInfo(); // if (user?.Id == null) return NotFound(); // return Ok(new UserInfo // { // Id = user.Id, // AccountId = user.AccountId, // SessionId = user.SessionId, // NickName = user.NickName, // FirstName = user.FirstName, // Patronymic = user.Patronymic, // LastName = user.LastName, // BirthDate = user.BirthDate, // LanguageId = user.LanguageId, // Email = user.Email, // IsAuthenticated = user.IsAuthenticated, // OnlineStatus = UserOnlineStatus.Online, // }); // } [HttpGet("Id")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(UserInfo))] public async Task GetUserId(CancellationToken cancellationToken) { return Ok(await _mediator.Send(new GetUserIdQuery(), cancellationToken)); } [AllowAnonymous] [HttpGet("Avatar")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))] public async Task GetAvatar([FromQuery]Guid userId) { var avatar = await _mediator.Send(new GetUserAvatarQuery { UserId = userId }); if (avatar == null) { return NotFound(); } else { return new FileStreamResult(avatar.Data, avatar.ContentType); } } [HttpPost("SetAvatar")] [ProducesResponseType((int)HttpStatusCode.OK)] public async Task SetAvatar(IFormFile file) { if (file.Length == 0) return BadRequest("File is empty."); await using var stream = file.OpenReadStream(); await _mediator.Send(new SetUserAvatarCommand { ContentType = file.ContentType, Extension = Path.GetExtension(file.FileName), Data = stream, }); return Ok(); } [HttpPost("DeleteAvatar")] [ProducesResponseType((int)HttpStatusCode.OK)] public async Task DeleteAvatar() { await _mediator.Send(new DeleteUserAvatarCommand()); return Ok(); } /* public string? NickName { get; set; } = default!; public string? FirstName { get; set; } public string? Patronymic { get; set; } public string? LastName { get; set; } public Guid? LanguageId { get; set; } */ [HttpPost("SetUserInformation")] [ProducesResponseType((int)HttpStatusCode.OK)] public async Task SetUserInformation(UserInformationEditModel model) { await _mediator.Send(new SetUserNameCommand { FirstName = model.FirstName, Patronymic = model.Patronymic, LastName = model.LastName, }); return Ok(); } [HttpPost("SetBirthDate")] [ProducesResponseType((int)HttpStatusCode.OK)] public async Task SetBirthDate(BirthDateEditModel model) { await _mediator.Send(new SetUserBirthDateCommand { BirthDate = model.BirthDate, }); return Ok(); } [HttpPost("SetLanguage")] [ProducesResponseType((int)HttpStatusCode.OK)] public async Task SetLanguage(LanguageEditModel model) { await _mediator.Send(new SetUserLanguageCommand { LanguageId = model.LanguageId, }); return Ok(); } }