Adds edition of user profile

This commit is contained in:
2024-09-03 20:59:43 -04:00
parent 012ad7fcf4
commit f12418bc79
28 changed files with 1398 additions and 182 deletions

View File

@@ -0,0 +1,30 @@
using Hutopy.Web.Common;
using Hutopy.Web.Features.Contents.Data;
namespace Hutopy.Web.Features.Contents.Handlers;
[PublicAPI]
public class GetCreatorProfileHandler(
ContentDbContext context)
: EndpointWithoutRequest<Creator>
{
public override void Configure()
{
Get("/api/creators/profile");
Options((o => o.WithTags("Creators")));
AllowAnonymous();
}
public override async Task HandleAsync(
CancellationToken ct)
{
var creator = await context
.Creators
.FindAsync(
[HttpContext.User.GetUserId()],
cancellationToken: ct);
if (creator is null) await SendNotFoundAsync(ct);
else await SendAsync(creator, cancellation: ct);
}
}

View File

@@ -1,6 +1,4 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Hutopy.Web.Features.Contents.Handlers.Models;
namespace Hutopy.Web.Features.Contents.Handlers.Models;
[PublicAPI]
public class ContentModel

View File

@@ -0,0 +1,43 @@
using Hutopy.Infrastructure.Identity;
using Hutopy.Web.Common;
namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI]
public record ChangeAddressRequest(
string? Address);
[PublicAPI]
public class ChangeAddressHandler(
ApplicationUserManager userManager)
: Endpoint<ChangeAddressRequest>
{
public override void Configure()
{
Post("/api/users/address");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
ChangeAddressRequest request,
CancellationToken ct)
{
var user = await userManager.FindByIdAsync(HttpContext.User.GetUserId().ToString());
if (user is null)
{
await SendNotFoundAsync(ct);
return;
}
user.Address = request.Address;
// TODO: check to see if identity resets the email-validated flag - @jonathan
var result = await userManager.UpdateAsync(user);
if (result.Succeeded)
await SendOkAsync(ct);
else
await SendUnauthorizedAsync(ct);
}
}

View File

@@ -0,0 +1,42 @@
using Hutopy.Infrastructure.Identity;
using Hutopy.Web.Common;
namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI]
public record ChangeAliasRequest(
string? Alias);
[PublicAPI]
public class ChangeAliasHandler(
ApplicationUserManager userManager)
: Endpoint<ChangeAliasRequest>
{
public override void Configure()
{
Post("/api/users/alias");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
ChangeAliasRequest request,
CancellationToken ct)
{
var user = await userManager.FindByIdAsync(HttpContext.User.GetUserId().ToString());
if (user is null)
{
await SendNotFoundAsync(ct);
return;
}
user.Alias = request.Alias;
var result = await userManager.UpdateAsync(user);
if (result.Succeeded)
await SendOkAsync(ct);
else
await SendUnauthorizedAsync(ct);
}
}

View File

@@ -0,0 +1,42 @@
using Hutopy.Infrastructure.Identity;
using Hutopy.Web.Common;
namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI]
public record ChangeBirthDateRequest(
DateTime BirthDate);
[PublicAPI]
public class ChangeBirthDateHandler(
ApplicationUserManager userManager)
: Endpoint<ChangeBirthDateRequest>
{
public override void Configure()
{
Post("/api/users/birthdate");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
ChangeBirthDateRequest request,
CancellationToken ct)
{
var user = await userManager.FindByIdAsync(HttpContext.User.GetUserId().ToString());
if (user is null)
{
await SendNotFoundAsync(ct);
return;
}
user.BirthDate = request.BirthDate;
var result = await userManager.UpdateAsync(user);
if (result.Succeeded)
await SendOkAsync(ct);
else
await SendUnauthorizedAsync(ct);
}
}

View File

@@ -0,0 +1,43 @@
using Hutopy.Infrastructure.Identity;
using Hutopy.Web.Common;
namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI]
public record ChangeEmailRequest(
string? Email);
[PublicAPI]
public class ChangeEmailHandler(
ApplicationUserManager userManager)
: Endpoint<ChangeEmailRequest>
{
public override void Configure()
{
Post("/api/users/email");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
ChangeEmailRequest request,
CancellationToken ct)
{
var user = await userManager.FindByIdAsync(HttpContext.User.GetUserId().ToString());
if (user is null)
{
await SendNotFoundAsync(ct);
return;
}
user.Email = request.Email;
// TODO: check to see if identity resets the email-validated flag - @jonathan
var result = await userManager.UpdateAsync(user);
if (result.Succeeded)
await SendOkAsync(ct);
else
await SendUnauthorizedAsync(ct);
}
}

View File

@@ -0,0 +1,44 @@
using Hutopy.Infrastructure.Identity;
using Hutopy.Web.Common;
namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI]
public record ChangeFullnameRequest(
string? Firstname,
string? Lastname);
[PublicAPI]
public class ChangeFullnameHandler(
ApplicationUserManager userManager)
: Endpoint<ChangeFullnameRequest>
{
public override void Configure()
{
Post("/api/users/fullname");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
ChangeFullnameRequest request,
CancellationToken ct)
{
var user = await userManager.FindByIdAsync(HttpContext.User.GetUserId().ToString());
if (user is null)
{
await SendNotFoundAsync(ct);
return;
}
user.Firstname = request.Firstname;
user.Lastname = request.Lastname;
var result = await userManager.UpdateAsync(user);
if (result.Succeeded)
await SendOkAsync(ct);
else
await SendUnauthorizedAsync(ct);
}
}

View File

@@ -0,0 +1,43 @@
using Hutopy.Infrastructure.Identity;
using Hutopy.Web.Common;
namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI]
public record ChangePhoneRequest(
string? PhoneNumber);
[PublicAPI]
public class ChangePhoneHandler(
ApplicationUserManager userManager)
: Endpoint<ChangePhoneRequest>
{
public override void Configure()
{
Post("/api/users/phone");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
ChangePhoneRequest request,
CancellationToken ct)
{
var user = await userManager.FindByIdAsync(HttpContext.User.GetUserId().ToString());
if (user is null)
{
await SendNotFoundAsync(ct);
return;
}
user.PhoneNumber = request.PhoneNumber;
// TODO: check to see if identity resets the email-validated flag - @jonathan
var result = await userManager.UpdateAsync(user);
if (result.Succeeded)
await SendOkAsync(ct);
else
await SendUnauthorizedAsync(ct);
}
}

View File

@@ -0,0 +1,48 @@
using Hutopy.Application.Common.Interfaces;
using Hutopy.Web.Features.Users.Handlers.Models;
namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI]
public class GetCurrentUserQueryHandler(
IIdentityService identityService
)
: EndpointWithoutRequest<UserDto>
{
public override void Configure()
{
Get("/api/users/profile");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
CancellationToken cancellationToken)
{
var userModel = await identityService.GetCurrentUserAsync();
if (userModel is null)
{
await SendNotFoundAsync(cancellationToken);
return;
}
var roles = await identityService.GetCurrentUserRolesAsync();
await SendOkAsync(
new UserDto
{
Id = userModel.Id,
Alias = userModel.Alias,
PortraitUrl = userModel.PortraitUrl,
Firstname = userModel.Firstname,
Lastname = userModel.Lastname,
Username = userModel.Username,
PhoneNumber = userModel.PhoneNumber,
Email = userModel.Email,
BirthDate = userModel.BirthDate,
Address = userModel.Address,
UserRoles = roles,
},
cancellationToken);
}
}

View File

@@ -0,0 +1,31 @@
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Web.Features.Users.Handlers;
[PublicAPI]
public class GetCurrentUserPortraitHandler(
IIdentityService identityService,
IBlobStorage blobStorage
)
: EndpointWithoutRequest<Stream>
{
public override void Configure()
{
Get("/api/users/portrait");
Options(o => o.WithTags("Users"));
}
public override async Task HandleAsync(
CancellationToken cancellationToken)
{
var identityUser = await identityService.GetCurrentUserAsync();
var stream = await blobStorage.DownloadFileAsync(
ContainerNames.Users,
$"{identityUser.Id.ToString()}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}",
cancellationToken);
await SendOkAsync(stream, cancellationToken);
}
}

View File

@@ -0,0 +1,16 @@
namespace Hutopy.Web.Features.Users.Handlers.Models;
public class UserDto
{
public Guid Id { get; init; }
public IList<string> UserRoles { get; init; } = [];
public string Username { get; init; } = null!;
public string? Alias { get; init; }
public string? PortraitUrl { get; init; }
public string? Firstname { get; init; }
public string? Lastname { get; init; }
public string? Email { get; init; }
public string? PhoneNumber { get; init; }
public DateTime? BirthDate { get; init; }
public string? Address { get; init; }
}

View File

@@ -0,0 +1,14 @@
namespace Hutopy.Web.Features.Wallets;
public class UserTransactionDto
{
public required decimal Amount { get; init; }
public string Currency { get; init; } = "cad";
public string TipMessage { get; init; } = string.Empty;
public DateTimeOffset Created { get; init; }
public bool IsConfirmed { get; init; }
}