Added profile ProfileColors and Profile images

This commit is contained in:
Dominic Villemure
2024-06-30 12:35:14 -04:00
parent 0e318eae32
commit 2603582286
27 changed files with 1017 additions and 119 deletions

View File

@@ -3,4 +3,6 @@
public static class CommonFileNames
{
public static string ProfilePicture = "profilePicture";
public static string BannerPicture = "bannerPicture";
public static string WebsiteIcon = "websiteIcon";
}

View File

@@ -9,10 +9,10 @@ public interface IIdentityService
Task<Result<string>> CreateUserAsync(Userinfo userInfo);
Task<Result<string>> CreateUserAsync(string email, string userName, string firstName, string lastName, string password);
Task<UserModel?> GetCurrentUserAsync();
Task<Result<string>> UpdateCurrentUserAsync(string id, string firstName, string lastName, string occupation,
string phoneNumber, string birthDate, string country, string city, string address, string about,
string description,
SocialNetworksModel socialNetworks);
Task<Result> UpdateCurrentUserBannerPictureUrlAsync(string url);
Task<Result> UpdateCurrentUserProfilePictureUrlAsync(string url);
Task<Result> UpdateCurrentUserWebsiteIconUrlAsync(string url);
Task<Result<string>> UpdateCurrentUserAsync(UserModel userModel);
Task<IList<string>> GetCurrentUserRolesAsync();
Task<UserModel?> FindUserByIdAsync(string id);
Task<UserModel?> FindUserByEmailAsync(string email);

View File

@@ -19,25 +19,26 @@ public class Result(
}
public class Result<T>(
T? value,
bool succeeded,
IEnumerable<string> errors)
{
public bool Succeeded { get; init; } = succeeded;
public string[] Errors { get; init; } = errors.ToArray();
public T? Value { get; set; }
public T? Value { get; set; } = value;
public T GetValueOrDefault()
{
return Value ?? default(T)!;
}
public static Result<T> Success()
public static Result<T> Success(T value)
{
return new Result<T>(true, Array.Empty<string>());
return new Result<T>(value, true, Array.Empty<string>());
}
public static Result<T> Failure(IEnumerable<string> errors)
public static Result<T> Failure(T value, IEnumerable<string> errors)
{
return new Result<T>(false, errors);
return new Result<T>(value, false, errors);
}
}

View File

@@ -2,22 +2,22 @@ using Hutopy.Application.Users.Models;
namespace Hutopy.Application.Common.Models;
// TODO: Review nullable affectation here
public class UserModel
{
public string? Id { get; set; }
public string? UserName { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string? Occupation { get; set; }
public string? Email { get; init; } = String.Empty;
public string? Phone { get; init; } = String.Empty;
public string? BirthDate { get; init; } = String.Empty;
public string? Country { get; init; } = String.Empty;
public string? City { get; init; } = String.Empty;
public string? Address { get; init; } = String.Empty;
public string? About { get; init; } = String.Empty;
public string? Description { get; init; } = String.Empty;
public string Id { get; set; } = string.Empty;
public string UserName { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Occupation { get; set; } = string.Empty;
public string Email { get; init; } = string.Empty;
public string Phone { get; init; } = string.Empty;
public string BirthDate { get; init; } = string.Empty;
public string Country { get; init; } = string.Empty;
public string City { get; init; } = string.Empty;
public string Address { get; init; } = string.Empty;
public string About { get; init; } = string.Empty;
public string Description { get; init; } = string.Empty;
public SocialNetworksModel SocialNetworks { get; init; } = new();
public string? PortraitUrl { get; set; }
public ProfileColorsModel ProfileColors { get; init; } = new();
public string ProfilePictureUrl { get; set; } = string.Empty;
}

View File

@@ -17,29 +17,29 @@ public class Data
public class Object
{
public string Id { get; set; } = String.Empty;
public string Id { get; set; } = string.Empty;
public int Amount { get; set; }
public BillingDetails Billing_details { get; set; } = new();
public string Calculated_statement_descriptor { get; set; } = String.Empty;
public string Currency { get; set; } = String.Empty;
public string Calculated_statement_descriptor { get; set; } = string.Empty;
public string Currency { get; set; } = string.Empty;
public bool Paid { get; set; }
public string Payment_intent { get; set; } = String.Empty;
public string Payment_method { get; set; } = String.Empty;
public string Receipt_url { get; set; } = String.Empty;
public string Status { get; set; } = String.Empty;
public string Failure_message { get; set; } = String.Empty;
public string Payment_intent { get; set; } = string.Empty;
public string Payment_method { get; set; } = string.Empty;
public string Receipt_url { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public string Failure_message { get; set; } = string.Empty;
}
public class BillingDetails
{
public string Email { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
public string Phone { get; set; } = String.Empty;
public string Email { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
}
public class Request
{
public string Id { get; set; } = String.Empty;
public string Id { get; set; } = string.Empty;
}
public class ConfirmStripeTransactionCommandHandler(

View File

@@ -16,6 +16,7 @@ public class UpdateCurrentUserCommand : IRequest<string>
public required string About { get; init; }
public required string Description { get; init; }
public required SocialNetworksModel SocialNetworks { get; init; }
public required ProfileColorsModel ProfileColors { get; init; }
}
public class UpdateCurrentUserCommandHandler(IApplicationDbContext context, IIdentityService identityService) :
@@ -27,10 +28,7 @@ public class UpdateCurrentUserCommandHandler(IApplicationDbContext context, IIde
if (identityUser?.Id is null) return string.Empty;
var result = await identityService.UpdateCurrentUserAsync(identityUser.Id, request.FirstName, request.LastName,
request.Occupation, request.PhoneNumber, request.BirthDate,
request.Country, request.City, request.Address, request.About,
request.Description, request.SocialNetworks);
var result = await identityService.UpdateCurrentUserAsync(identityUser);
await context.SaveChangesAsync(cancellationToken);

View File

@@ -0,0 +1,27 @@
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.Users.Commands;
public class UploadBannerPictureCommand : IRequest<string>
{
public required Stream BannerPicture { get; init; }
}
public class UploadBannerPictureCommandHandler(IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadBannerPictureCommand, string>
{
public async Task<string> Handle(UploadBannerPictureCommand request, CancellationToken cancellationToken)
{
var identityUser = await identityService.GetCurrentUserAsync();
var currentUserId = new Guid(identityUser?.Id ?? "").ToString();
var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.BannerPicture}";
var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.BannerPicture);
await identityService.UpdateCurrentUserBannerPictureUrlAsync(url);
return url;
}
}

View File

@@ -19,6 +19,8 @@ public class UploadProfilePictureCommandHandler(IIdentityService identityService
var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.ProfilePicture);
await identityService.UpdateCurrentUserProfilePictureUrlAsync(url);
return url;
}
}

View File

@@ -0,0 +1,27 @@
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces;
namespace Hutopy.Application.Users.Commands;
public class UploadWebsiteIconCommand : IRequest<string>
{
public required Stream WebsiteIcon { get; init; }
}
public class UploadWebsiteIconCommandHandler(IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadWebsiteIconCommand, string>
{
public async Task<string> Handle(UploadWebsiteIconCommand request, CancellationToken cancellationToken)
{
var identityUser = await identityService.GetCurrentUserAsync();
var currentUserId = new Guid(identityUser?.Id ?? "").ToString();
var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.WebsiteIcon}";
var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.WebsiteIcon);
await identityService.UpdateCurrentUserWebsiteIconUrlAsync(url);
return url;
}
}

View File

@@ -0,0 +1,9 @@
namespace Hutopy.Application.Users.Models;
public class ProfileColorsModel
{
public string BannerTop { get; init; } = String.Empty;
public string BannerBottom { get; init; } = String.Empty;
public string Accent { get; init; } = String.Empty;
public string Menu { get; init; } = String.Empty;
}

View File

@@ -2,12 +2,12 @@
public class SocialNetworksModel
{
public string FacebookUrl { get; init; } = String.Empty;
public string InstagramUrl { get; init; } = String.Empty;
public string XUrl { get; init; } = String.Empty;
public string LinkedInUrl { get; init; } = String.Empty;
public string TikTokUrl { get; init; } = String.Empty;
public string YoutubeUrl { get; init; } = String.Empty;
public string RedditUrl { get; init; } = String.Empty;
public string YourWebsiteUrl { get; init; } = String.Empty;
public string FacebookUrl { get; init; } = string.Empty;
public string InstagramUrl { get; init; } = string.Empty;
public string XUrl { get; init; } = string.Empty;
public string LinkedInUrl { get; init; } = string.Empty;
public string TikTokUrl { get; init; } = string.Empty;
public string YoutubeUrl { get; init; } = string.Empty;
public string RedditUrl { get; init; } = string.Empty;
public string YourWebsiteUrl { get; init; } = string.Empty;
}

View File

@@ -7,16 +7,16 @@ public class UserDto
public Guid Id { get; init; }
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string UserName { get; init; } = String.Empty;
public string Occupation { get; init; } = String.Empty;
public string Email { get; init; } = String.Empty;
public string Phone { get; init; } = String.Empty;
public string BirthDate { get; init; } = String.Empty;
public string Country { get; init; } = String.Empty;
public string City { get; init; } = String.Empty;
public string Address { get; init; } = String.Empty;
public string About { get; init; } = String.Empty;
public string Description { get; init; } = String.Empty;
public string UserName { get; init; } = string.Empty;
public string Occupation { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
public string Phone { get; init; } = string.Empty;
public string BirthDate { get; init; } = string.Empty;
public string Country { get; init; } = string.Empty;
public string City { get; init; } = string.Empty;
public string Address { get; init; } = string.Empty;
public string About { get; init; } = string.Empty;
public string Description { get; init; } = string.Empty;
public SocialNetworksModel SocialNetworks { get; init; } = new();
public List<UserTransactionDto> UserTransactions { get; init; } = [];
public IList<string> UserRoles { get; init; } = [];

View File

@@ -1,3 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Hutopy.Application.Common.Interfaces;

View File

@@ -1,4 +1,7 @@
using Hutopy.Domain.Constants;
using System;
using System.Linq;
using System.Threading.Tasks;
using Hutopy.Domain.Constants;
using Hutopy.Infrastructure.Identity;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;

View File

@@ -12,5 +12,15 @@ public class ApplicationUserConfiguration : IEntityTypeConfiguration<Application
builder
.OwnsOne(u => u.SocialNetworks)
.ToTable($"{nameof(ApplicationUser)}_SocialNetworks");
// Relationship between ApplicationUser and ProfileColors
builder
.OwnsOne(u => u.ProfileColors)
.ToTable($"{nameof(ApplicationUser)}_ProfileColors");
// Relationship between ApplicationUser and StoredDataUrls
builder
.OwnsOne(u => u.StoredDataUrls)
.ToTable($"{nameof(ApplicationUser)}_StoredDataUrls");
}
}

View File

@@ -1,4 +1,5 @@
using Hutopy.Application.Common.Interfaces;
using System;
using Hutopy.Application.Common.Interfaces;
using Hutopy.Domain.Constants;
using Hutopy.Infrastructure.AzureBlob;
using Hutopy.Infrastructure.Data;

View File

@@ -15,4 +15,6 @@ public class ApplicationUser : IdentityUser
public string About { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public SocialNetworks SocialNetworks { get; set; } = new();
public ProfileColors ProfileColors { get; set; } = new();
public StoredDataUrls StoredDataUrls { get; set; } = new();
}

View File

@@ -1,6 +1,8 @@
using System.Diagnostics;
using System;
using System.Collections.Generic;
using Google.Apis.Oauth2.v2.Data;
using System.Security.Claims;
using System.Threading.Tasks;
using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Models;
using Hutopy.Application.Users.Models;
@@ -64,10 +66,8 @@ public class IdentityService(
var applicationResult = identityResult.ToApplicationResult();
var result = new Result<string>(applicationResult.Succeeded, applicationResult.Errors);
var result = new Result<string>(applicationUser.Id, applicationResult.Succeeded, applicationResult.Errors);
result.Value = applicationUser.Id;
return result;
}
@@ -83,51 +83,53 @@ public class IdentityService(
var response = await userManager.CreateAsync(applicationUser, password);
var result = new Result<string>(response.Succeeded, response.ToApplicationResult().Errors);
result.Value = applicationUser.Id;
var result = new Result<string>(applicationUser.Id, response.Succeeded, response.ToApplicationResult().Errors);
return result;
}
public async Task<Result<string>> UpdateCurrentUserAsync(string id, string firstName, string lastName, string occupation,
string phoneNumber, string birthDate, string country, string city, string address, string about, string description,
SocialNetworksModel socialNetworks)
public async Task<Result<string>> UpdateCurrentUserAsync(UserModel userModel)
{
var applicationUser = await userManager.FindByIdAsync(id);
var applicationUser = await userManager.FindByIdAsync(userModel.Id);
if (applicationUser is null) return Result<string>.Failure(new[] { "User not found." });
if (applicationUser is null) return Result<string>.Failure("", new[] { "User not found." });
applicationUser.FirstName = firstName;
applicationUser.LastName = lastName;
applicationUser.Occupation = occupation;
applicationUser.PhoneNumber = phoneNumber;
applicationUser.BirthDate = birthDate;
applicationUser.Country = country;
applicationUser.City = city;
applicationUser.Address = address;
applicationUser.About = about;
applicationUser.Description = description;
applicationUser.SocialNetworks = new SocialNetworks()
applicationUser.FirstName = userModel.FirstName;
applicationUser.LastName = userModel.LastName;
applicationUser.Occupation = userModel.Occupation;
applicationUser.PhoneNumber = userModel.Phone;
applicationUser.BirthDate = userModel.BirthDate;
applicationUser.Country = userModel.Country;
applicationUser.City = userModel.City;
applicationUser.Address = userModel.Address;
applicationUser.About = userModel.About;
applicationUser.Description = userModel.Description;
applicationUser.SocialNetworks = new SocialNetworks
{
FacebookUrl = socialNetworks.FacebookUrl,
InstagramUrl = socialNetworks.InstagramUrl,
XUrl = socialNetworks.XUrl,
LinkedInUrl = socialNetworks.LinkedInUrl,
TikTokUrl = socialNetworks.TikTokUrl,
YoutubeUrl = socialNetworks.YoutubeUrl,
RedditUrl = socialNetworks.RedditUrl,
YourWebsiteUrl = socialNetworks.YourWebsiteUrl
FacebookUrl = userModel.SocialNetworks.FacebookUrl,
InstagramUrl = userModel.SocialNetworks.InstagramUrl,
XUrl = userModel.SocialNetworks.XUrl,
LinkedInUrl = userModel.SocialNetworks.LinkedInUrl,
TikTokUrl = userModel.SocialNetworks.TikTokUrl,
YoutubeUrl = userModel.SocialNetworks.YoutubeUrl,
RedditUrl = userModel.SocialNetworks.RedditUrl,
YourWebsiteUrl = userModel.SocialNetworks.YourWebsiteUrl
};
applicationUser.ProfileColors = new ProfileColors
{
BannerTop = userModel.ProfileColors.BannerTop,
BannerBottom = userModel.ProfileColors.BannerBottom,
Accent = userModel.ProfileColors.Accent,
Menu = userModel.ProfileColors.Menu
};
var response = await userManager.UpdateAsync(applicationUser);
var applicationResult = response.ToApplicationResult();
var result = new Result<string>(applicationResult.Succeeded,
var result = new Result<string>(userModel.Id, applicationResult.Succeeded,
applicationResult.Errors);
result.Value = id;
return result;
}
@@ -140,12 +142,12 @@ public class IdentityService(
var userModel = new UserModel
{
Id = response.Id,
UserName = response.UserName,
UserName = response.UserName ?? string.Empty,
FirstName = response.FirstName,
LastName = response.LastName,
Email = response.Email,
Email = response.Email ?? string.Empty,
Occupation = response.Occupation,
Phone = response.PhoneNumber,
Phone = response.PhoneNumber ?? string.Empty,
BirthDate = response.BirthDate,
Country = response.Country,
City = response.City,
@@ -162,6 +164,57 @@ public class IdentityService(
YoutubeUrl = response.SocialNetworks.YoutubeUrl,
RedditUrl = response.SocialNetworks.RedditUrl,
YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl,
},
ProfileColors = new ProfileColorsModel
{
BannerTop = response.ProfileColors.BannerTop,
BannerBottom = response.ProfileColors.BannerBottom,
Accent = response.ProfileColors.Accent,
Menu = response.ProfileColors.Menu
}
};
return userModel;
}
public async Task<UserModel?> FindUserByEmailAsync(string email)
{
var response = await userManager.FindByEmailAsync(email);
if (response == null) return null;
var userModel = new UserModel
{
Id = response.Id,
UserName = response.UserName ?? string.Empty,
FirstName = response.FirstName,
LastName = response.LastName,
Email = response.Email ?? string.Empty,
Occupation = response.Occupation,
Phone = response.PhoneNumber ?? string.Empty,
BirthDate = response.BirthDate,
Country = response.Country,
City = response.City,
Address = response.Address,
About = response.About,
Description = response.Description,
SocialNetworks = new SocialNetworksModel
{
FacebookUrl = response.SocialNetworks.FacebookUrl,
InstagramUrl = response.SocialNetworks.InstagramUrl,
XUrl = response.SocialNetworks.XUrl,
LinkedInUrl = response.SocialNetworks.LinkedInUrl,
TikTokUrl = response.SocialNetworks.TikTokUrl,
YoutubeUrl = response.SocialNetworks.YoutubeUrl,
RedditUrl = response.SocialNetworks.RedditUrl,
YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl,
},
ProfileColors = new ProfileColorsModel
{
BannerTop = response.ProfileColors.BannerTop,
BannerBottom = response.ProfileColors.BannerBottom,
Accent = response.ProfileColors.Accent,
Menu = response.ProfileColors.Menu
}
};
@@ -179,22 +232,49 @@ public class IdentityService(
return await FindUserByIdAsync(currentUserId);
}
public async Task<UserModel?> FindUserByEmailAsync(string email)
public async Task<Result> UpdateCurrentUserBannerPictureUrlAsync(string url)
{
var response = await userManager.FindByEmailAsync(email);
var userModel = await GetCurrentUserAsync();
if (userModel is null) return Result.Failure(new[] { "User not found." });
if (response == null) return null;
var applicationUser = await userManager.FindByIdAsync(userModel.Id);
if (applicationUser is null) return Result.Failure(new[] { "ApplicationUser not found." });
var userModel = new UserModel
{
Id = response.Id,
UserName = response.UserName,
FirstName = response.FirstName,
LastName = response.LastName,
Email = response.Email
};
applicationUser.StoredDataUrls.BannerPictureUrl = url;
var response = await userManager.UpdateAsync(applicationUser);
return userModel;
return response.ToApplicationResult();
}
public async Task<Result> UpdateCurrentUserProfilePictureUrlAsync(string url)
{
var userModel = await GetCurrentUserAsync();
if (userModel is null) return Result.Failure(new[] { "User not found." });
var applicationUser = await userManager.FindByIdAsync(userModel.Id);
if (applicationUser is null) return Result.Failure(new[] { "ApplicationUser not found." });
applicationUser.StoredDataUrls.ProfilePictureUrl = url;
var response = await userManager.UpdateAsync(applicationUser);
return response.ToApplicationResult();
}
public async Task<Result> UpdateCurrentUserWebsiteIconUrlAsync(string url)
{
var userModel = await GetCurrentUserAsync();
if (userModel is null) return Result.Failure(new[] { "User not found." });
var applicationUser = await userManager.FindByIdAsync(userModel.Id);
if (applicationUser is null) return Result.Failure(new[] { "ApplicationUser not found." });
applicationUser.StoredDataUrls.WebsiteIconUrl = url;
var response = await userManager.UpdateAsync(applicationUser);
return response.ToApplicationResult();
}
public async Task<bool> IsInRoleAsync(string userId, string role)
@@ -293,7 +373,7 @@ public class IdentityService(
email: user.Email,
firstname: user.FirstName,
lastname: user.LastName,
portraitUrl: user.PortraitUrl);
portraitUrl: user.ProfilePictureUrl);
return token;
}

View File

@@ -0,0 +1,9 @@
namespace Hutopy.Infrastructure.Identity.OwnedEntities;
public class ProfileColors
{
public string BannerTop { get; init; } = string.Empty;
public string BannerBottom { get; init; } = string.Empty;
public string Accent { get; init; } = string.Empty;
public string Menu { get; init; } = string.Empty;
}

View File

@@ -2,12 +2,12 @@ namespace Hutopy.Infrastructure.Identity.OwnedEntities;
public class SocialNetworks
{
public string FacebookUrl { get; init; } = String.Empty;
public string InstagramUrl { get; init; } = String.Empty;
public string XUrl { get; init; } = String.Empty;
public string LinkedInUrl { get; init; } = String.Empty;
public string TikTokUrl { get; init; } = String.Empty;
public string YoutubeUrl { get; init; } = String.Empty;
public string RedditUrl { get; init; } = String.Empty;
public string YourWebsiteUrl { get; init; } = String.Empty;
public string FacebookUrl { get; init; } = string.Empty;
public string InstagramUrl { get; init; } = string.Empty;
public string XUrl { get; init; } = string.Empty;
public string LinkedInUrl { get; init; } = string.Empty;
public string TikTokUrl { get; init; } = string.Empty;
public string YoutubeUrl { get; init; } = string.Empty;
public string RedditUrl { get; init; } = string.Empty;
public string YourWebsiteUrl { get; init; } = string.Empty;
}

View File

@@ -0,0 +1,8 @@
namespace Hutopy.Infrastructure.Identity.OwnedEntities;
public class StoredDataUrls
{
public string BannerPictureUrl { get; set; } = string.Empty;
public string ProfilePictureUrl { get; set; } = string.Empty;
public string WebsiteIconUrl { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,557 @@
// <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("20240630163057_AddMoreInformationsToUser")]
partial class AddMoreInformationsToUser
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("EmailAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
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<string>("ApplicationUserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<DateTimeOffset>("Created")
.HasColumnType("datetimeoffset");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsConfirmed")
.HasColumnType("bit");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("datetimeoffset");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
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.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("About")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("Address")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("BirthDate")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");
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")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");
b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");
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.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
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("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", 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<string>("RoleId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", 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<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
b.Property<string>("RoleId")
.HasColumnType("nvarchar(450)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("nvarchar(450)");
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("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.ProfileColors", "ProfileColors", b1 =>
{
b1.Property<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("Accent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("BannerBottom")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("BannerTop")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("Menu")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.HasKey("ApplicationUserId");
b1.ToTable("ApplicationUser_ProfileColors", (string)null);
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
});
b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.SocialNetworks", "SocialNetworks", b1 =>
{
b1.Property<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("FacebookUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("InstagramUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("LinkedInUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("RedditUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("TikTokUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("XUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("YourWebsiteUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("YoutubeUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.HasKey("ApplicationUserId");
b1.ToTable("ApplicationUser_SocialNetworks", (string)null);
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
});
b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.StoredDataUrls", "StoredDataUrls", b1 =>
{
b1.Property<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("BannerPictureUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("ProfilePictureUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("WebsiteIconUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.HasKey("ApplicationUserId");
b1.ToTable("ApplicationUser_StoredDataUrls", (string)null);
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
});
b.Navigation("ProfileColors")
.IsRequired();
b.Navigation("SocialNetworks")
.IsRequired();
b.Navigation("StoredDataUrls")
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", 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<string>", 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,77 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Hutopy.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddMoreInformationsToUser : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ApplicationUser_ProfileColors",
columns: table => new
{
ApplicationUserId = table.Column<string>(type: "nvarchar(450)", nullable: false),
BannerTop = table.Column<string>(type: "nvarchar(max)", nullable: false),
BannerBottom = table.Column<string>(type: "nvarchar(max)", nullable: false),
Accent = table.Column<string>(type: "nvarchar(max)", nullable: false),
Menu = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApplicationUser_ProfileColors", x => x.ApplicationUserId);
table.ForeignKey(
name: "FK_ApplicationUser_ProfileColors_AspNetUsers_ApplicationUserId",
column: x => x.ApplicationUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.Sql(@"
INSERT INTO ApplicationUser_ProfileColors (ApplicationUserId, BannerTop, BannerBottom, Accent, Menu)
SELECT Id, '', '', '', ''
FROM AspNetUsers
");
migrationBuilder.CreateTable(
name: "ApplicationUser_StoredDataUrls",
columns: table => new
{
ApplicationUserId = table.Column<string>(type: "nvarchar(450)", nullable: false),
BannerPictureUrl = table.Column<string>(type: "nvarchar(max)", nullable: false),
ProfilePictureUrl = table.Column<string>(type: "nvarchar(max)", nullable: false),
WebsiteIconUrl = table.Column<string>(type: "nvarchar(max)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApplicationUser_StoredDataUrls", x => x.ApplicationUserId);
table.ForeignKey(
name: "FK_ApplicationUser_StoredDataUrls_AspNetUsers_ApplicationUserId",
column: x => x.ApplicationUserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.Sql(@"
INSERT INTO ApplicationUser_StoredDataUrls (ApplicationUserId, BannerPictureUrl, ProfilePictureUrl, WebsiteIconUrl)
SELECT Id, '', '', ''
FROM AspNetUsers
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ApplicationUser_ProfileColors");
migrationBuilder.DropTable(
name: "ApplicationUser_StoredDataUrls");
}
}
}

View File

@@ -389,6 +389,35 @@ namespace Hutopy.Infrastructure.Migrations
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
{
b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.ProfileColors", "ProfileColors", b1 =>
{
b1.Property<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("Accent")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("BannerBottom")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("BannerTop")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("Menu")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.HasKey("ApplicationUserId");
b1.ToTable("ApplicationUser_ProfileColors", (string)null);
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
});
b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.SocialNetworks", "SocialNetworks", b1 =>
{
b1.Property<string>("ApplicationUserId")
@@ -434,8 +463,39 @@ namespace Hutopy.Infrastructure.Migrations
.HasForeignKey("ApplicationUserId");
});
b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.StoredDataUrls", "StoredDataUrls", b1 =>
{
b1.Property<string>("ApplicationUserId")
.HasColumnType("nvarchar(450)");
b1.Property<string>("BannerPictureUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("ProfilePictureUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.Property<string>("WebsiteIconUrl")
.IsRequired()
.HasColumnType("nvarchar(max)");
b1.HasKey("ApplicationUserId");
b1.ToTable("ApplicationUser_StoredDataUrls", (string)null);
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
});
b.Navigation("ProfileColors")
.IsRequired();
b.Navigation("SocialNetworks")
.IsRequired();
b.Navigation("StoredDataUrls")
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>

View File

@@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Stripe;
using Stripe.Checkout;
using Hutopy.Application.Common.Interfaces;

View File

@@ -71,7 +71,7 @@ public class GoogleController(IIdentityService identityService, IHttpClientFacto
user.Email,
user.FirstName,
user.LastName,
user.PortraitUrl);
user.ProfilePictureUrl);
return Ok(new { accessToken = token, email });
}

View File

@@ -8,6 +8,9 @@ public class UpdateMyUser : EndpointGroupBase
{
app.MapGroup(this)
.RequireAuthorization()
.MapPost(UpdateCurrentUserProfilePicture, "/profile-picture")
.MapPost(UpdateCurrentUserBannerPicture, "/banner-picture")
.MapPost(UpdateCurrentUserWebsiteIcon, "/website-icon")
.MapPatch("/profile", UpdateCurrentUser);
}
@@ -16,5 +19,21 @@ public class UpdateMyUser : EndpointGroupBase
return await sender.Send(command);
}
private static async Task<string> UpdateCurrentUserProfilePicture(ISender sender, Stream stream)
{
var command = new UploadProfilePictureCommand { ProfilePicture = stream };
return await sender.Send(command);
}
private static async Task<string> UpdateCurrentUserBannerPicture(ISender sender, Stream stream)
{
var command = new UploadBannerPictureCommand { BannerPicture = stream };
return await sender.Send(command);
}
private static async Task<string> UpdateCurrentUserWebsiteIcon(ISender sender, Stream stream)
{
var command = new UploadWebsiteIconCommand { WebsiteIcon = stream };
return await sender.Send(command);
}
}