using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using VoidCat.Database; using VoidCat.Model; using VoidCat.Services.Abstractions; using VoidCat.Services.Files; using UserFlags = VoidCat.Database.UserFlags; namespace VoidCat.Controllers; [Route("user/{id}")] public class UserController : Controller { private readonly IUserStore _store; private readonly IUserUploadsStore _userUploads; private readonly IEmailVerification _emailVerification; private readonly FileInfoManager _fileInfoManager; public UserController(IUserStore store, IUserUploadsStore userUploads, IEmailVerification emailVerification, FileInfoManager fileInfoManager) { _store = store; _userUploads = userUploads; _emailVerification = emailVerification; _fileInfoManager = fileInfoManager; } /// /// Return user profile /// /// /// You do not need to be logged in to return a user profile if their profile is set to public. /// /// You may also use `me` as the `id` to get the logged in users profile. /// /// User id to load /// [HttpGet] public async Task GetUser([FromRoute] string id) { var loggedUser = HttpContext.GetUserId(); var isMe = id.Equals("me", StringComparison.InvariantCultureIgnoreCase); if (isMe && !loggedUser.HasValue) return Unauthorized(); var requestedId = isMe ? loggedUser!.Value : id.FromBase58Guid(); var user = await _store.Get(requestedId); if (user == default) return NotFound(); if (loggedUser != requestedId && !user.Flags.HasFlag(UserFlags.PublicProfile) && !HttpContext.IsRole(Roles.Admin)) return NotFound(); var isMyProfile = requestedId == user.Id; return Json(user!.ToApiUser(isMyProfile)); } /// /// Update profile settings /// /// /// User id /// /// [HttpPost] public async Task UpdateUser([FromRoute] string id, [FromBody] ApiUser user) { var loggedUser = await GetAuthorizedUser(id); if (loggedUser == default) return Unauthorized(); if (!loggedUser.Flags.HasFlag(UserFlags.EmailVerified)) return Forbid(); loggedUser.Avatar = user.Avatar; loggedUser.DisplayName = user.Name ?? "void user"; loggedUser.Flags = UserFlags.EmailVerified | (user.PublicProfile ? UserFlags.PublicProfile : 0) | (user.PublicUploads ? UserFlags.PublicUploads : 0); await _store.UpdateProfile(loggedUser); return Ok(); } /// /// Return a list of files which the user has uploaded /// /// /// This will return files if the profile has public uploads set on their profile. /// Otherwise you can return your own uploaded files if you are logged in. /// /// User id /// Page request /// [HttpPost] [Route("files")] public async Task ListUserFiles([FromRoute] string id, [FromBody] PagedRequest request) { var loggedUser = HttpContext.GetUserId(); var isAdmin = HttpContext.IsRole(Roles.Admin); var user = await GetRequestedUser(id); if (user == default) return NotFound(); // not logged in user files, check public flag var canViewUploads = loggedUser == user.Id || isAdmin; if (!canViewUploads && !user.Flags.HasFlag(UserFlags.PublicUploads)) return Forbid(); var results = await _userUploads.ListFiles(id.FromBase58Guid(), request); var files = await results.Data.ToListAsync(); var fileInfo = await _fileInfoManager.Get(files.ToArray(), false); return Json(new RenderedResults() { PageSize = results.PageSize, Page = results.Page, TotalResults = results.TotalResults, Results = fileInfo.ToList() }); } /// /// Send a verification code for a specific user /// /// User id to send code for /// [HttpGet] [Route("verify")] public async Task SendVerificationCode([FromRoute] string id) { var user = await GetAuthorizedUser(id); if (user == default) return Unauthorized(); var isEmailVerified = (user?.Flags.HasFlag(UserFlags.EmailVerified) ?? false); if (isEmailVerified) return UnprocessableEntity(); await _emailVerification.SendNewCode(user!); return Accepted(); } /// /// Confirm email verification code /// /// User id to verify /// Verification code to check /// [HttpPost] [Route("verify")] public async Task VerifyCode([FromRoute] string id, [FromBody] VerifyCodeRequest req) { var user = await GetAuthorizedUser(id); if (user == default) return Unauthorized(); if (!await _emailVerification.VerifyCode(user, req.Code)) return BadRequest(); user.Flags |= UserFlags.EmailVerified; await _store.UpdateProfile(user); return Accepted(); } private async Task GetAuthorizedUser(string id) { var loggedUser = HttpContext.GetUserId(); var gid = id.FromBase58Guid(); var user = await _store.Get(gid); return user?.Id != loggedUser ? default : user; } private async Task GetRequestedUser(string id) { var gid = id.FromBase58Guid(); return await _store.Get(gid); } public class VerifyCodeRequest { [JsonProperty("code")] [JsonConverter(typeof(Base58GuidConverter))] public Guid Code { get; init; } } }