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

@@ -3,14 +3,13 @@ namespace Hutopy.Application.Common.Models;
public class UserModel
{
public Guid Id { get; set; }
public string UserName { get; init; } = null!;
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? Occupation { get; init; }
public string? Firstname { get; init; }
public string? Lastname { get; init; }
public string? Email { get; init; }
public string? PhoneNumber { get; init; }
public string? BirthDate { get; init; }
public DateTime? BirthDate { get; init; }
public string? Address { get; init; }
}

View File

@@ -1,50 +0,0 @@
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.Users.Queries.GetCurrentUser;
public record GetCurrentUserQuery : IRequest<UserDto>;
public class GetCurrentUserQueryHandler(
IApplicationDbContext context,
IMapper mapper,
IIdentityService identityService
)
: IRequestHandler<GetCurrentUserQuery, UserDto>
{
public async Task<UserDto?> Handle(GetCurrentUserQuery request, CancellationToken cancellationToken)
{
var userModel = await identityService.GetCurrentUserAsync();
if (userModel is null) return null;
var transactions = await context
.UserTransactions
.Where(x => x.ApplicationUserId == userModel.Id)
.OrderBy(x => x.LastModifiedAt)
.ProjectTo<UserTransactionDto>(mapper.ConfigurationProvider)
.Where(x => x.IsConfirmed == true)
.ToListAsync(cancellationToken);
var roles = await identityService.GetCurrentUserRolesAsync();
var user = new UserDto
{
Id = userModel.Id,
Alias = userModel.Alias,
PortraitUrl = userModel.PortraitUrl,
FirstName = userModel.FirstName,
LastName = userModel.LastName,
UserName = userModel.UserName,
Occupation = userModel.Occupation,
PhoneNumber = userModel.PhoneNumber,
Email = userModel.Email,
BirthDate = userModel.BirthDate,
Address = userModel.Address,
UserTransactions = transactions,
TotalBalance = transactions.Sum(x => x.Amount),
UserRoles = roles,
};
return user;
}
}

View File

@@ -1,23 +0,0 @@
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.Users.Queries.GetCurrentUser;
public record GetCurrentUserProfilePictureQuery : IRequest<Stream>;
public class GetCurrentUserProfilePictureQueryHandler(
IIdentityService identityService,
IBlobStorage blobStorage
)
: IRequestHandler<GetCurrentUserProfilePictureQuery, Stream>
{
public async Task<Stream> Handle(GetCurrentUserProfilePictureQuery request, CancellationToken cancellationToken)
{
var identityUser = await identityService.GetCurrentUserAsync();
return await blobStorage.DownloadFileAsync(
ContainerNames.Users,
$"{identityUser.Id.ToString()}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}",
cancellationToken);
}
}

View File

@@ -1,19 +0,0 @@
namespace Hutopy.Application.Users.Queries.GetCurrentUser;
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? Occupation { get; init; }
public string? Email { get; init; }
public string? PhoneNumber { get; init; }
public string? BirthDate { get; init; }
public string? Address { get; init; }
public List<UserTransactionDto> UserTransactions { get; init; } = [];
public required decimal TotalBalance { get; init; }
}

View File

@@ -17,9 +17,8 @@ public static class UserDtoExtensions
new()
{
Id = model.Id,
FirstName = model.FirstName,
LastName = model.LastName,
UserName = model.UserName,
Occupation = model.Occupation
FirstName = model.Firstname,
LastName = model.Lastname,
UserName = model.Username
};
}

View File

@@ -6,10 +6,9 @@ namespace Hutopy.Infrastructure.Identity;
public class ApplicationUser : IdentityUser<Guid>
{
[MaxLength(255)] public string? Alias { get; set; }
[MaxLength(255)] public string? FirstName { get; set; }
[MaxLength(255)] public string? LastName { get; set; }
[MaxLength(255)] public string? Occupation { get; set; }
[MaxLength(255)] public string? BirthDate { get; set; }
[MaxLength(255)] public string? Firstname { get; set; }
[MaxLength(255)] public string? Lastname { get; set; }
public DateTime? BirthDate { get; set; }
[MaxLength(255)] public string? Address { get; set; }
[MaxLength(255)] public string? PortraitUrl { get; set; }
}

View File

@@ -36,13 +36,12 @@ public class IdentityService(
return new()
{
Id = user.Id,
UserName = user.UserName!,
Username = user.UserName!,
PhoneNumber = user.PhoneNumber,
Email = user.Email,
Alias = user.Alias,
FirstName = user.FirstName,
LastName = user.LastName,
Occupation = user.Occupation,
Firstname = user.Firstname,
Lastname = user.Lastname,
BirthDate = user.BirthDate,
Address = user.Address,
PortraitUrl = user.PortraitUrl
@@ -55,8 +54,8 @@ public class IdentityService(
{
UserName = userInfo.Name,
Email = userInfo.Email,
FirstName = userInfo.GivenName,
LastName = userInfo.FamilyName
Firstname = userInfo.GivenName,
Lastname = userInfo.FamilyName
};
var password = Guid.NewGuid().ToString("N")[..32];
@@ -75,7 +74,7 @@ public class IdentityService(
{
var applicationUser = new ApplicationUser
{
UserName = userName, Email = email, FirstName = firstName, LastName = lastName
UserName = userName, Email = email, Firstname = firstName, Lastname = lastName
};
var response = await userManager.CreateAsync(applicationUser, password);
@@ -95,9 +94,8 @@ public class IdentityService(
applicationUser.Email = userModel.Email;
applicationUser.PhoneNumber = userModel.PhoneNumber;
applicationUser.Alias = userModel.Alias;
applicationUser.FirstName = userModel.FirstName;
applicationUser.LastName = userModel.LastName;
applicationUser.Occupation = userModel.Occupation;
applicationUser.Firstname = userModel.Firstname;
applicationUser.Lastname = userModel.Lastname;
applicationUser.BirthDate = userModel.BirthDate;
applicationUser.Address = userModel.Address;
applicationUser.PortraitUrl = userModel.PortraitUrl;
@@ -120,14 +118,13 @@ public class IdentityService(
var userModel = new UserModel
{
Id = response.Id,
UserName = response.UserName ?? string.Empty,
Username = response.UserName ?? string.Empty,
PhoneNumber = response.PhoneNumber ?? string.Empty,
Email = response.Email ?? string.Empty,
PortraitUrl = response.PortraitUrl,
Alias = response.Alias,
FirstName = response.FirstName,
LastName = response.LastName,
Occupation = response.Occupation,
Firstname = response.Firstname,
Lastname = response.Lastname,
BirthDate = response.BirthDate,
Address = response.Address,
};
@@ -275,8 +272,8 @@ public class IdentityService(
userId: user.Id.ToString(),
email: user.Email,
alias: user.Alias,
firstname: user.FirstName,
lastname: user.LastName,
firstname: user.Firstname,
lastname: user.Lastname,
portraitUrl: user.PortraitUrl);
return token;

View File

@@ -0,0 +1,436 @@
// <auto-generated />
using System;
using Hutopy.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Hutopy.Infrastructure.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240903230821_ChangeBirthDateToDateTime")]
partial class ChangeBirthDateToDateTime
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("datetimeoffset");
b.Property<Guid?>("CreatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModifiedAt")
.HasColumnType("datetimeoffset");
b.Property<Guid?>("LastModifiedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ReasonToJoin")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SocialNetworkAccount")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("FutureCreators");
});
modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<decimal>("Amount")
.HasPrecision(18, 2)
.HasColumnType("decimal(18,2)");
b.Property<Guid>("ApplicationUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("datetimeoffset");
b.Property<Guid?>("CreatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsConfirmed")
.HasColumnType("bit");
b.Property<DateTimeOffset>("LastModifiedAt")
.HasColumnType("datetimeoffset");
b.Property<Guid?>("LastModifiedBy")
.HasColumnType("uniqueidentifier");
b.Property<bool>("Paid")
.HasColumnType("bit");
b.Property<string>("StripeBillingDetailEmail")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeBillingDetailName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeChargeId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeEventId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripePaymentIntent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripePaymentMethod")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeReceiptUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("TipMessage")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("ApplicationUserId");
b.ToTable("UserTransactions");
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("Address")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("Alias")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<DateTime?>("BirthDate")
.HasMaxLength(255)
.HasColumnType("datetime2");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("Firstname")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("Lastname")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Occupation")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("PortraitUrl")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("RoleId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("RoleId")
.HasColumnType("uniqueidentifier");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class ChangeBirthDateToDateTime : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "City",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "Country",
table: "AspNetUsers");
migrationBuilder.RenameColumn(
name: "LastName",
table: "AspNetUsers",
newName: "Lastname");
migrationBuilder.RenameColumn(
name: "FirstName",
table: "AspNetUsers",
newName: "Firstname");
migrationBuilder.AlterColumn<DateTime>(
name: "BirthDate",
table: "AspNetUsers",
type: "datetime2",
maxLength: 255,
nullable: true,
oldClrType: typeof(string),
oldType: "nvarchar(255)",
oldMaxLength: 255,
oldNullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Lastname",
table: "AspNetUsers",
newName: "LastName");
migrationBuilder.RenameColumn(
name: "Firstname",
table: "AspNetUsers",
newName: "FirstName");
migrationBuilder.AlterColumn<string>(
name: "BirthDate",
table: "AspNetUsers",
type: "nvarchar(255)",
maxLength: 255,
nullable: true,
oldClrType: typeof(DateTime),
oldType: "datetime2",
oldMaxLength: 255,
oldNullable: true);
migrationBuilder.AddColumn<string>(
name: "City",
table: "AspNetUsers",
type: "nvarchar(255)",
maxLength: 255,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Country",
table: "AspNetUsers",
type: "nvarchar(255)",
maxLength: 255,
nullable: true);
}
}
}

View File

@@ -0,0 +1,432 @@
// <auto-generated />
using System;
using Hutopy.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Hutopy.Infrastructure.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240903231429_RemoveOccupation")]
partial class RemoveOccupation
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("datetimeoffset");
b.Property<Guid?>("CreatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModifiedAt")
.HasColumnType("datetimeoffset");
b.Property<Guid?>("LastModifiedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ReasonToJoin")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SocialNetworkAccount")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("FutureCreators");
});
modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<decimal>("Amount")
.HasPrecision(18, 2)
.HasColumnType("decimal(18,2)");
b.Property<Guid>("ApplicationUserId")
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("CreatedAt")
.HasColumnType("datetimeoffset");
b.Property<Guid?>("CreatedBy")
.HasColumnType("uniqueidentifier");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsConfirmed")
.HasColumnType("bit");
b.Property<DateTimeOffset>("LastModifiedAt")
.HasColumnType("datetimeoffset");
b.Property<Guid?>("LastModifiedBy")
.HasColumnType("uniqueidentifier");
b.Property<bool>("Paid")
.HasColumnType("bit");
b.Property<string>("StripeBillingDetailEmail")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeBillingDetailName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeChargeId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeEventId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripePaymentIntent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripePaymentMethod")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("StripeReceiptUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("TipMessage")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("ApplicationUserId");
b.ToTable("UserTransactions");
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationRole", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("Address")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("Alias")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<DateTime?>("BirthDate")
.HasMaxLength(255)
.HasColumnType("datetime2");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("Firstname")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("Lastname")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
b.Property<string>("PortraitUrl")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("RoleId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");
b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.Property<Guid>("RoleId")
.HasColumnType("uniqueidentifier");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uniqueidentifier");
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("Name")
.HasColumnType("nvarchar(450)");
b.Property<string>("Value")
.HasColumnType("nvarchar(max)");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class RemoveOccupation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Occupation",
table: "AspNetUsers");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Occupation",
table: "AspNetUsers",
type: "nvarchar(255)",
maxLength: 255,
nullable: true);
}
}
}

View File

@@ -188,22 +188,14 @@ namespace Hutopy.Infrastructure.Migrations
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("BirthDate")
b.Property<DateTime?>("BirthDate")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("City")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
.HasColumnType("datetime2");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Country")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
@@ -211,11 +203,11 @@ namespace Hutopy.Infrastructure.Migrations
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
b.Property<string>("Firstname")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("LastName")
b.Property<string>("Lastname")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
@@ -233,10 +225,6 @@ namespace Hutopy.Infrastructure.Migrations
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<string>("Occupation")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");

View File

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

View File

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

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

@@ -1,6 +1,4 @@
using Hutopy.Domain.Entities;
namespace Hutopy.Application.Users.Queries.GetCurrentUser;
namespace Hutopy.Web.Features.Wallets;
public class UserTransactionDto
{
@@ -13,11 +11,4 @@ public class UserTransactionDto
public DateTimeOffset Created { get; init; }
public bool IsConfirmed { get; init; }
private class Mapping : Profile
{
public Mapping()
{
CreateMap<UserTransaction, UserTransactionDto>();
}
}
}

View File

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

View File

@@ -73,7 +73,7 @@ public partial class Testing
var user = new ApplicationUser
{
UserName = userName, Email = userName, FirstName = "FirstName", LastName = "LastName"
UserName = userName, Email = userName, Firstname = "FirstName", Lastname = "LastName"
};
var result = await userManager.CreateAsync(user, password);