Adds edition of user profile
This commit is contained in:
@@ -75,8 +75,8 @@ public class GoogleController(
|
||||
user.Id.ToString(),
|
||||
user.Email,
|
||||
user.Alias,
|
||||
user.FirstName,
|
||||
user.LastName,
|
||||
user.Firstname,
|
||||
user.Lastname,
|
||||
user.PortraitUrl);
|
||||
|
||||
return Ok(new { accessToken = token, email });
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using Hutopy.Application.Users.Queries.GetCurrentUser;
|
||||
using Hutopy.Web.Infrastructure;
|
||||
|
||||
namespace Hutopy.Web.Endpoints;
|
||||
|
||||
public class GetMyUser : EndpointGroupBase
|
||||
{
|
||||
public override void Map(WebApplication app)
|
||||
{
|
||||
app.MapGroup(this)
|
||||
.RequireAuthorization()
|
||||
.MapGet(GetCurrentUser)
|
||||
.MapGet(GetCurrentUserProfilePicture, "profile-picture");
|
||||
}
|
||||
|
||||
private static async Task<UserDto> GetCurrentUser(ISender sender, [AsParameters] GetCurrentUserQuery query)
|
||||
{
|
||||
return await sender.Send(query);
|
||||
}
|
||||
|
||||
private static async Task<Stream> GetCurrentUserProfilePicture(ISender sender, [AsParameters] GetCurrentUserProfilePictureQuery query)
|
||||
{
|
||||
return await sender.Send(query);
|
||||
}
|
||||
}
|
||||
30
src/Web/Features/Contents/Handlers/GetCreatorProfile.cs
Normal file
30
src/Web/Features/Contents/Handlers/GetCreatorProfile.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
43
src/Web/Features/Users/Handlers/ChangeAddress.cs
Normal file
43
src/Web/Features/Users/Handlers/ChangeAddress.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
42
src/Web/Features/Users/Handlers/ChangeAlias.cs
Normal file
42
src/Web/Features/Users/Handlers/ChangeAlias.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
42
src/Web/Features/Users/Handlers/ChangeBirthDate.cs
Normal file
42
src/Web/Features/Users/Handlers/ChangeBirthDate.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
43
src/Web/Features/Users/Handlers/ChangeEmail.cs
Normal file
43
src/Web/Features/Users/Handlers/ChangeEmail.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
44
src/Web/Features/Users/Handlers/ChangeFullname.cs
Normal file
44
src/Web/Features/Users/Handlers/ChangeFullname.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
43
src/Web/Features/Users/Handlers/ChangePhone.cs
Normal file
43
src/Web/Features/Users/Handlers/ChangePhone.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
48
src/Web/Features/Users/Handlers/GetCurrentUser.cs
Normal file
48
src/Web/Features/Users/Handlers/GetCurrentUser.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
16
src/Web/Features/Users/Handlers/Models/UserDto.cs
Normal file
16
src/Web/Features/Users/Handlers/Models/UserDto.cs
Normal 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; }
|
||||
}
|
||||
14
src/Web/Features/Wallets/UserTransactionDto.cs
Normal file
14
src/Web/Features/Wallets/UserTransactionDto.cs
Normal 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; }
|
||||
}
|
||||
@@ -113,7 +113,7 @@ internal class TestDataSeeder(
|
||||
SubjectId = content.Id,
|
||||
CreatedAt = currentDate,
|
||||
CreatedBy = author.Id,
|
||||
CreatedByName = author.Alias ?? $"{author.FirstName} {author.LastName}",
|
||||
CreatedByName = author.Alias ?? $"{author.Firstname} {author.Lastname}",
|
||||
CreatedByPortraitUrl = author.PortraitUrl,
|
||||
Value = $"Message #{m} on {content.Title}"
|
||||
};
|
||||
@@ -143,7 +143,7 @@ internal class TestDataSeeder(
|
||||
SubjectId = content.Id,
|
||||
ParentId = parent.Id,
|
||||
CreatedBy = author.Id,
|
||||
CreatedByName = author.Alias ?? $"{author.FirstName} {author.LastName}",
|
||||
CreatedByName = author.Alias ?? $"{author.Firstname} {author.Lastname}",
|
||||
CreatedByPortraitUrl = author.PortraitUrl,
|
||||
CreatedAt = currentDate,
|
||||
Value = $"Reply {r} to {parent.Value} on {content.Title}"
|
||||
@@ -165,8 +165,8 @@ internal class TestDataSeeder(
|
||||
Email = $"{name}@test",
|
||||
EmailConfirmed = true,
|
||||
Alias = name,
|
||||
FirstName = $"FirstName of {name}",
|
||||
LastName = $"LastName of {name}",
|
||||
Firstname = $"FirstName of {name}",
|
||||
Lastname = $"LastName of {name}",
|
||||
PortraitUrl = portraitUrl
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user