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; }
}
}