Merged PR 88: UpdateCurrentUser into Main
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
<PackageVersion Include="Azure.Identity" Version="1.11.0" />
|
<PackageVersion Include="Azure.Identity" Version="1.11.0" />
|
||||||
<PackageVersion Include="Azure.Storage.Blobs" Version="12.20.0" />
|
<PackageVersion Include="Azure.Storage.Blobs" Version="12.20.0" />
|
||||||
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
|
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
|
||||||
|
<PackageVersion Include="FastEndpoints" Version="5.26.0" />
|
||||||
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
|
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
|
||||||
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
||||||
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.8.1" />
|
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.8.1" />
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
Directory.Packages.props = Directory.Packages.props
|
Directory.Packages.props = Directory.Packages.props
|
||||||
global.json = global.json
|
global.json = global.json
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
|
start-infrastructure.sh = start-infrastructure.sh
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{4E4EE20C-F06A-4A1B-851F-C5577796941C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{4E4EE20C-F06A-4A1B-851F-C5577796941C}"
|
||||||
|
|||||||
@@ -3,4 +3,6 @@
|
|||||||
public static class CommonFileNames
|
public static class CommonFileNames
|
||||||
{
|
{
|
||||||
public static string ProfilePicture = "profilePicture";
|
public static string ProfilePicture = "profilePicture";
|
||||||
|
public static string BannerPicture = "bannerPicture";
|
||||||
|
public static string WebsiteIcon = "websiteIcon";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,27 @@
|
|||||||
using Google.Apis.Oauth2.v2.Data;
|
using Google.Apis.Oauth2.v2.Data;
|
||||||
using Hutopy.Application.Common.Models;
|
using Hutopy.Application.Common.Models;
|
||||||
|
using Hutopy.Application.Users.Models;
|
||||||
|
|
||||||
namespace Hutopy.Application.Common.Interfaces;
|
namespace Hutopy.Application.Common.Interfaces;
|
||||||
|
|
||||||
public interface IIdentityService
|
public interface IIdentityService
|
||||||
{
|
{
|
||||||
Task<string?> GetUserNameAsync(string userId);
|
Task<Result<string>> CreateUserAsync(Userinfo userInfo);
|
||||||
Task<Result> CreateUserAsync(string email, string userName, string firstName, string lastName, string password);
|
Task<Result<string>> CreateUserAsync(string email, string userName, string firstName, string lastName, string password);
|
||||||
Task<UserModel?> FindUserByIdAsync(string id);
|
|
||||||
Task<UserModel?> GetCurrentUserAsync();
|
Task<UserModel?> GetCurrentUserAsync();
|
||||||
Task<UserModel?> FindUserByEmailAsync(string id);
|
Task<Result> UpdateCurrentUserBannerPictureUrlAsync(string url);
|
||||||
Task<string?> LoginAsync(string email, string password);
|
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);
|
||||||
Task<UserModel?> GetUserByUserNameAsync(string userName);
|
Task<UserModel?> GetUserByUserNameAsync(string userName);
|
||||||
|
Task<string?> LoginAsync(string email, string password);
|
||||||
Task<bool> IsInRoleAsync(string userId, string role);
|
Task<bool> IsInRoleAsync(string userId, string role);
|
||||||
Task<bool> AuthorizeAsync(string userId, string policyName);
|
Task<bool> AuthorizeAsync(string userId, string policyName);
|
||||||
|
Task<string?> GetUserNameAsync(string userId);
|
||||||
|
|
||||||
Task<Result> AddRoleAsync(string userId, string role);
|
Task<Result> AddRoleAsync(string userId, string role);
|
||||||
Task<IList<string>> GetCurrentUserRolesAsync();
|
|
||||||
Task<(Result Result, string UserId)> CreateUserAsync(Userinfo userInfo);
|
|
||||||
Task<Result> DeleteUserAsync(string userId);
|
Task<Result> DeleteUserAsync(string userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ public class Result(
|
|||||||
IEnumerable<string> errors)
|
IEnumerable<string> errors)
|
||||||
{
|
{
|
||||||
public bool Succeeded { get; init; } = succeeded;
|
public bool Succeeded { get; init; } = succeeded;
|
||||||
|
|
||||||
public string[] Errors { get; init; } = errors.ToArray();
|
public string[] Errors { get; init; } = errors.ToArray();
|
||||||
|
|
||||||
public static Result Success()
|
public static Result Success()
|
||||||
{
|
{
|
||||||
return new Result(true, Array.Empty<string>());
|
return new Result(true, Array.Empty<string>());
|
||||||
@@ -18,3 +17,28 @@ public class Result(
|
|||||||
return new Result(false, errors);
|
return new Result(false, errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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; } = value;
|
||||||
|
|
||||||
|
public T GetValueOrDefault()
|
||||||
|
{
|
||||||
|
return Value ?? default(T)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<T> Success(T value)
|
||||||
|
{
|
||||||
|
return new Result<T>(value, true, Array.Empty<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<T> Failure(T value, IEnumerable<string> errors)
|
||||||
|
{
|
||||||
|
return new Result<T>(value, false, errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
|
using Hutopy.Application.Users.Models;
|
||||||
|
|
||||||
namespace Hutopy.Application.Common.Models;
|
namespace Hutopy.Application.Common.Models;
|
||||||
|
|
||||||
public class UserModel
|
public class UserModel
|
||||||
{
|
{
|
||||||
public string? Id { get; set; }
|
public string Id { get; set; } = string.Empty;
|
||||||
public string? UserName { get; set; }
|
public string UserName { get; set; } = string.Empty;
|
||||||
public string? FirstName { get; set; }
|
public string FirstName { get; set; } = string.Empty;
|
||||||
public string? LastName { get; set; }
|
public string LastName { get; set; } = string.Empty;
|
||||||
public string? Email { get; set; }
|
public string Occupation { get; set; } = string.Empty;
|
||||||
|
public string Email { get; init; } = string.Empty;
|
||||||
|
public string PhoneNumber { 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 ProfileColorsModel ProfileColors { get; init; } = new();
|
||||||
|
public StoredDataUrlsModel StoredDataUrls { get; init; } = new();
|
||||||
|
public string ProfilePictureUrl { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,29 +17,29 @@ public class Data
|
|||||||
|
|
||||||
public class Object
|
public class Object
|
||||||
{
|
{
|
||||||
public string Id { get; set; } = String.Empty;
|
public string Id { get; set; } = string.Empty;
|
||||||
public int Amount { get; set; }
|
public int Amount { get; set; }
|
||||||
public BillingDetails Billing_details { get; set; } = new();
|
public BillingDetails Billing_details { get; set; } = new();
|
||||||
public string Calculated_statement_descriptor { get; set; } = String.Empty;
|
public string Calculated_statement_descriptor { get; set; } = string.Empty;
|
||||||
public string Currency { get; set; } = String.Empty;
|
public string Currency { get; set; } = string.Empty;
|
||||||
public bool Paid { get; set; }
|
public bool Paid { get; set; }
|
||||||
public string Payment_intent { get; set; } = String.Empty;
|
public string Payment_intent { get; set; } = string.Empty;
|
||||||
public string Payment_method { get; set; } = String.Empty;
|
public string Payment_method { get; set; } = string.Empty;
|
||||||
public string Receipt_url { get; set; } = String.Empty;
|
public string Receipt_url { get; set; } = string.Empty;
|
||||||
public string Status { get; set; } = String.Empty;
|
public string Status { get; set; } = string.Empty;
|
||||||
public string Failure_message { get; set; } = String.Empty;
|
public string Failure_message { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BillingDetails
|
public class BillingDetails
|
||||||
{
|
{
|
||||||
public string Email { get; set; } = String.Empty;
|
public string Email { get; set; } = string.Empty;
|
||||||
public string Name { get; set; } = String.Empty;
|
public string Name { get; set; } = string.Empty;
|
||||||
public string Phone { get; set; } = String.Empty;
|
public string Phone { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Request
|
public class Request
|
||||||
{
|
{
|
||||||
public string Id { get; set; } = String.Empty;
|
public string Id { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConfirmStripeTransactionCommandHandler(
|
public class ConfirmStripeTransactionCommandHandler(
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
|
|
||||||
namespace Hutopy.Application.Users.Commands;
|
namespace Hutopy.Application.Users.Commands;
|
||||||
public record LoginCommand : IRequest<string>
|
|
||||||
|
public record LoginCommand(
|
||||||
|
string Email,
|
||||||
|
string Password)
|
||||||
|
: IRequest<LoginResponse>;
|
||||||
|
|
||||||
|
public record LoginResponse(
|
||||||
|
string AccessToken,
|
||||||
|
string RefreshToken);
|
||||||
|
|
||||||
|
public class LoginCommandHandler(
|
||||||
|
IApplicationDbContext Context,
|
||||||
|
IIdentityService identityService)
|
||||||
|
: IRequestHandler<LoginCommand, LoginResponse>
|
||||||
{
|
{
|
||||||
public required string EmailAddress { get; init; }
|
public async Task<LoginResponse> Handle(LoginCommand request, CancellationToken cancellationToken)
|
||||||
public required string Password { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LoginCommandHandler : IRequestHandler<LoginCommand, string>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly IIdentityService _identityService;
|
|
||||||
|
|
||||||
public LoginCommandHandler(IApplicationDbContext context, IIdentityService identityService)
|
|
||||||
{
|
{
|
||||||
_context = context;
|
var accessToken = await identityService.LoginAsync(request.Email, request.Password);
|
||||||
_identityService = identityService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> Handle(LoginCommand request, CancellationToken cancellationToken)
|
if (string.IsNullOrWhiteSpace(accessToken)) throw new InvalidOperationException("Invalid login credentials");
|
||||||
{
|
|
||||||
var jwt = await _identityService.LoginAsync(request.EmailAddress, request.Password);
|
|
||||||
|
|
||||||
return jwt ?? "Invalid login credentials";
|
return new LoginResponse(accessToken, string.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
src/Application/Users/Commands/UpdateCurrentUserCommand.cs
Normal file
52
src/Application/Users/Commands/UpdateCurrentUserCommand.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Hutopy.Application.Common.Interfaces;
|
||||||
|
using Hutopy.Application.Common.Models;
|
||||||
|
using Hutopy.Application.Users.Models;
|
||||||
|
|
||||||
|
namespace Hutopy.Application.Users.Commands;
|
||||||
|
|
||||||
|
public class UpdateCurrentUserCommand : IRequest<string>
|
||||||
|
{
|
||||||
|
public required string FirstName { get; init; }
|
||||||
|
public required string LastName { get; init; }
|
||||||
|
public required string Occupation { get; init; }
|
||||||
|
public required string PhoneNumber { get; init; }
|
||||||
|
public required string BirthDate { get; init; }
|
||||||
|
public required string Country { get; init; }
|
||||||
|
public required string City { get; init; }
|
||||||
|
public required string Address { get; init; }
|
||||||
|
public required string About { get; init; }
|
||||||
|
public required string Description { get; init; }
|
||||||
|
public required SocialNetworksModel SocialNetworks { get; init; }
|
||||||
|
public required ProfileColorsModel ProfileColors { get; init; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
private class Mapping : Profile
|
||||||
|
{
|
||||||
|
public Mapping()
|
||||||
|
{
|
||||||
|
CreateMap<UpdateCurrentUserCommand, UserModel>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateCurrentUserCommandHandler(IApplicationDbContext context, IIdentityService identityService, IMapper mapper) :
|
||||||
|
IRequestHandler<UpdateCurrentUserCommand, string>
|
||||||
|
{
|
||||||
|
public async Task<string> Handle(UpdateCurrentUserCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var identityUser = await identityService.GetCurrentUserAsync();
|
||||||
|
|
||||||
|
if (identityUser?.Id is null) return string.Empty;
|
||||||
|
|
||||||
|
var userModel = mapper.Map<UserModel>(request);
|
||||||
|
userModel.Id = identityUser.Id;
|
||||||
|
|
||||||
|
var result = await identityService.UpdateCurrentUserAsync(userModel);
|
||||||
|
|
||||||
|
await context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
return result.GetValueOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
27
src/Application/Users/Commands/UploadBannerPicture.cs
Normal file
27
src/Application/Users/Commands/UploadBannerPicture.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -19,6 +19,8 @@ public class UploadProfilePictureCommandHandler(IIdentityService identityService
|
|||||||
|
|
||||||
var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.ProfilePicture);
|
var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.ProfilePicture);
|
||||||
|
|
||||||
|
await identityService.UpdateCurrentUserProfilePictureUrlAsync(url);
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/Application/Users/Commands/UploadWebsiteIcon.cs
Normal file
27
src/Application/Users/Commands/UploadWebsiteIcon.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
9
src/Application/Users/Models/ProfileColorsModel.cs
Normal file
9
src/Application/Users/Models/ProfileColorsModel.cs
Normal 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;
|
||||||
|
}
|
||||||
13
src/Application/Users/Models/SocialNetworksModel.cs
Normal file
13
src/Application/Users/Models/SocialNetworksModel.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Hutopy.Application.Users.Models;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
8
src/Application/Users/Models/StoredDataUrlsModel.cs
Normal file
8
src/Application/Users/Models/StoredDataUrlsModel.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Hutopy.Application.Users.Models;
|
||||||
|
|
||||||
|
public class StoredDataUrlsModel
|
||||||
|
{
|
||||||
|
public string BannerPictureUrl { get; set; } = string.Empty;
|
||||||
|
public string ProfilePictureUrl { get; set; } = string.Empty;
|
||||||
|
public string WebsiteIconUrl { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -8,14 +8,14 @@ public class GetCurrentUserQueryHandler(
|
|||||||
IApplicationDbContext context,
|
IApplicationDbContext context,
|
||||||
IMapper mapper,
|
IMapper mapper,
|
||||||
IIdentityService identityService
|
IIdentityService identityService
|
||||||
)
|
)
|
||||||
: IRequestHandler<GetCurrentUserQuery, UserDto>
|
: IRequestHandler<GetCurrentUserQuery, UserDto>
|
||||||
{
|
{
|
||||||
public async Task<UserDto> Handle(GetCurrentUserQuery request, CancellationToken cancellationToken)
|
public async Task<UserDto> Handle(GetCurrentUserQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var identityUser = await identityService.GetCurrentUserAsync();
|
var identityUser = await identityService.GetCurrentUserAsync();
|
||||||
var currentUserId = new Guid(identityUser?.Id ?? "");
|
var currentUserId = Guid.Parse(identityUser!.Id!);
|
||||||
|
|
||||||
var transactions = await context.UserTransactions
|
var transactions = await context.UserTransactions
|
||||||
.Where(x => x.ApplicationUserId == currentUserId.ToString())
|
.Where(x => x.ApplicationUserId == currentUserId.ToString())
|
||||||
.OrderBy(x => x.LastModified)
|
.OrderBy(x => x.LastModified)
|
||||||
@@ -28,12 +28,24 @@ public class GetCurrentUserQueryHandler(
|
|||||||
var user = new UserDto
|
var user = new UserDto
|
||||||
{
|
{
|
||||||
Id = currentUserId,
|
Id = currentUserId,
|
||||||
FirstName = identityUser?.FirstName ?? "",
|
FirstName = identityUser.FirstName ?? "",
|
||||||
LastName = identityUser?.LastName ?? "",
|
LastName = identityUser.LastName ?? "",
|
||||||
UserName =identityUser?.UserName ?? "",
|
UserName = identityUser.UserName ?? "",
|
||||||
|
Occupation = identityUser.Occupation ?? "",
|
||||||
|
PhoneNumber = identityUser.PhoneNumber ?? "",
|
||||||
|
Email = identityUser.Email ?? "",
|
||||||
|
BirthDate = identityUser.BirthDate ?? "",
|
||||||
|
Country = identityUser.Country ?? "",
|
||||||
|
City = identityUser.City ?? "",
|
||||||
|
Address = identityUser.Address ?? "",
|
||||||
|
About = identityUser.About ?? "",
|
||||||
|
Description = identityUser.Description ?? "",
|
||||||
|
SocialNetworks = identityUser.SocialNetworks,
|
||||||
|
ProfileColors = identityUser.ProfileColors,
|
||||||
|
StoredDataUrls = identityUser.StoredDataUrls,
|
||||||
UserTransactions = transactions,
|
UserTransactions = transactions,
|
||||||
TotalBalance = transactions.Sum(x => x.Amount),
|
TotalBalance = transactions.Sum(x => x.Amount),
|
||||||
UserRoles = roles
|
UserRoles = roles,
|
||||||
};
|
};
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Hutopy.Application.Users.Models;
|
||||||
|
|
||||||
namespace Hutopy.Application.Users.Queries.GetCurrentUser;
|
namespace Hutopy.Application.Users.Queries.GetCurrentUser;
|
||||||
|
|
||||||
public class UserDto
|
public class UserDto
|
||||||
@@ -5,9 +7,20 @@ public class UserDto
|
|||||||
public Guid Id { get; init; }
|
public Guid Id { get; init; }
|
||||||
public required string FirstName { get; init; }
|
public required string FirstName { get; init; }
|
||||||
public required string LastName { get; init; }
|
public required string LastName { get; init; }
|
||||||
public string UserName { 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 PhoneNumber { 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 ProfileColorsModel ProfileColors { get; init; } = new();
|
||||||
|
public StoredDataUrlsModel StoredDataUrls { get; init; } = new();
|
||||||
public List<UserTransactionDto> UserTransactions { get; init; } = [];
|
public List<UserTransactionDto> UserTransactions { get; init; } = [];
|
||||||
public IList<string> UserRoles { get; init; } = [];
|
public IList<string> UserRoles { get; init; } = [];
|
||||||
public required decimal TotalBalance { get; init; }
|
public required decimal TotalBalance { get; init; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Azure.Storage.Blobs;
|
using Azure.Storage.Blobs;
|
||||||
using Azure.Storage.Blobs.Models;
|
using Azure.Storage.Blobs.Models;
|
||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
|
|||||||
@@ -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 Hutopy.Infrastructure.Identity;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|||||||
@@ -12,5 +12,15 @@ public class ApplicationUserConfiguration : IEntityTypeConfiguration<Application
|
|||||||
builder
|
builder
|
||||||
.OwnsOne(u => u.SocialNetworks)
|
.OwnsOne(u => u.SocialNetworks)
|
||||||
.ToTable($"{nameof(ApplicationUser)}_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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Hutopy.Application.Common.Interfaces;
|
using System;
|
||||||
|
using Hutopy.Application.Common.Interfaces;
|
||||||
using Hutopy.Domain.Constants;
|
using Hutopy.Domain.Constants;
|
||||||
using Hutopy.Infrastructure.AzureBlob;
|
using Hutopy.Infrastructure.AzureBlob;
|
||||||
using Hutopy.Infrastructure.Data;
|
using Hutopy.Infrastructure.Data;
|
||||||
@@ -15,22 +16,13 @@ namespace Hutopy.Infrastructure;
|
|||||||
|
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services,
|
||||||
|
IConfiguration configuration)
|
||||||
{
|
{
|
||||||
// Replace password in the connection string with env var in local environment.
|
// Replace password in the connection string with env var in local environment.
|
||||||
// Prod will use the connectionString stored in the vault with password in it directly.
|
// Prod will use the connectionString stored in the vault with password in it directly.
|
||||||
var connectionString = configuration.GetConnectionString("DefaultConnection") ?? "";
|
var connectionString = configuration.GetConnectionString("DefaultConnection")
|
||||||
|
?? throw new InvalidOperationException("Missing ConnectionString: DefaultConnection");
|
||||||
var dbPassword = configuration["DB_PASSWORD"] ?? "";
|
|
||||||
var dbHost = configuration["DB_HOST"] ?? "localhost";
|
|
||||||
|
|
||||||
if (dbPassword != string.Empty)
|
|
||||||
{
|
|
||||||
connectionString = connectionString.Replace("{DB_PASSWORD}", dbPassword);
|
|
||||||
connectionString = connectionString.Replace("{DB_HOST}", dbHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
Guard.Against.Null(connectionString, message: "Connection string 'DefaultConnection' not found.");
|
|
||||||
|
|
||||||
services.AddScoped<ISaveChangesInterceptor, AuditableEntityInterceptor>();
|
services.AddScoped<ISaveChangesInterceptor, AuditableEntityInterceptor>();
|
||||||
services.AddScoped<ISaveChangesInterceptor, DispatchDomainEventsInterceptor>();
|
services.AddScoped<ISaveChangesInterceptor, DispatchDomainEventsInterceptor>();
|
||||||
@@ -49,15 +41,9 @@ public static class DependencyInjection
|
|||||||
.AddBearerToken(IdentityConstants.BearerScheme);
|
.AddBearerToken(IdentityConstants.BearerScheme);
|
||||||
|
|
||||||
services.AddAuthorizationBuilder();
|
services.AddAuthorizationBuilder();
|
||||||
|
|
||||||
services.AddIdentityCore<ApplicationUser>(options =>
|
services
|
||||||
{
|
.AddIdentityCore<ApplicationUser>()
|
||||||
options.Password.RequireDigit = false;
|
|
||||||
options.Password.RequireLowercase = false;
|
|
||||||
options.Password.RequireUppercase = false;
|
|
||||||
options.Password.RequireNonAlphanumeric = false;
|
|
||||||
options.Password.RequiredLength = 8;
|
|
||||||
})
|
|
||||||
.AddRoles<IdentityRole>()
|
.AddRoles<IdentityRole>()
|
||||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||||
.AddApiEndpoints()
|
.AddApiEndpoints()
|
||||||
|
|||||||
@@ -7,5 +7,14 @@ public class ApplicationUser : IdentityUser
|
|||||||
{
|
{
|
||||||
public string FirstName { get; set; } = string.Empty;
|
public string FirstName { get; set; } = string.Empty;
|
||||||
public string LastName { get; set; } = string.Empty;
|
public string LastName { get; set; } = string.Empty;
|
||||||
|
public string Occupation { get; set; } = string.Empty;
|
||||||
|
public string BirthDate { get; set; } = string.Empty;
|
||||||
|
public string Country { get; set; } = string.Empty;
|
||||||
|
public string City { get; set; } = string.Empty;
|
||||||
|
public string Address { get; set; } = string.Empty;
|
||||||
|
public string About { get; set; } = string.Empty;
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
public SocialNetworks SocialNetworks { get; set; } = new();
|
public SocialNetworks SocialNetworks { get; set; } = new();
|
||||||
|
public ProfileColors ProfileColors { get; set; } = new();
|
||||||
|
public StoredDataUrls StoredDataUrls { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Google.Apis.Oauth2.v2.Data;
|
using Google.Apis.Oauth2.v2.Data;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
using Hutopy.Application.Common.Models;
|
using Hutopy.Application.Common.Models;
|
||||||
|
using Hutopy.Application.Users.Models;
|
||||||
|
using Hutopy.Infrastructure.Identity.OwnedEntities;
|
||||||
using Hutopy.Infrastructure.Utils;
|
using Hutopy.Infrastructure.Utils;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@@ -44,23 +49,10 @@ public class IdentityService(
|
|||||||
|
|
||||||
return userModel;
|
return userModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(Result Result, string UserId)> CreateUserAsync(string userName, string password)
|
|
||||||
{
|
|
||||||
var user = new ApplicationUser
|
|
||||||
{
|
|
||||||
UserName = userName,
|
|
||||||
Email = userName,
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = await userManager.CreateAsync(user, password);
|
|
||||||
|
|
||||||
return (result.ToApplicationResult(), user.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(Result Result, string UserId)> CreateUserAsync(Userinfo userInfo)
|
public async Task<Result<string>> CreateUserAsync(Userinfo userInfo)
|
||||||
{
|
{
|
||||||
var user = new ApplicationUser
|
var applicationUser = new ApplicationUser
|
||||||
{
|
{
|
||||||
UserName = userInfo.Name,
|
UserName = userInfo.Name,
|
||||||
Email = userInfo.Email,
|
Email = userInfo.Email,
|
||||||
@@ -70,12 +62,16 @@ public class IdentityService(
|
|||||||
|
|
||||||
var password = Guid.NewGuid().ToString("N")[..32];
|
var password = Guid.NewGuid().ToString("N")[..32];
|
||||||
|
|
||||||
var result = await userManager.CreateAsync(user, password);
|
var identityResult = await userManager.CreateAsync(applicationUser, password);
|
||||||
|
|
||||||
return (result.ToApplicationResult(), user.Id);
|
var applicationResult = identityResult.ToApplicationResult();
|
||||||
|
|
||||||
|
var result = new Result<string>(applicationUser.Id, applicationResult.Succeeded, applicationResult.Errors);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result> CreateUserAsync(string email, string userName, string firstName, string lastName, string password)
|
public async Task<Result<string>> CreateUserAsync(string email, string userName, string firstName, string lastName, string password)
|
||||||
{
|
{
|
||||||
var applicationUser = new ApplicationUser
|
var applicationUser = new ApplicationUser
|
||||||
{
|
{
|
||||||
@@ -87,7 +83,54 @@ public class IdentityService(
|
|||||||
|
|
||||||
var response = await userManager.CreateAsync(applicationUser, password);
|
var response = await userManager.CreateAsync(applicationUser, password);
|
||||||
|
|
||||||
return response.ToApplicationResult();
|
var result = new Result<string>(applicationUser.Id, response.Succeeded, response.ToApplicationResult().Errors);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Result<string>> UpdateCurrentUserAsync(UserModel userModel)
|
||||||
|
{
|
||||||
|
var applicationUser = await userManager.FindByIdAsync(userModel.Id);
|
||||||
|
|
||||||
|
if (applicationUser is null) return Result<string>.Failure("", new[] { "User not found." });
|
||||||
|
|
||||||
|
applicationUser.FirstName = userModel.FirstName;
|
||||||
|
applicationUser.LastName = userModel.LastName;
|
||||||
|
applicationUser.Occupation = userModel.Occupation;
|
||||||
|
applicationUser.PhoneNumber = userModel.PhoneNumber;
|
||||||
|
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 = 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>(userModel.Id, applicationResult.Succeeded,
|
||||||
|
applicationResult.Errors);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserModel?> FindUserByIdAsync(string id)
|
public async Task<UserModel?> FindUserByIdAsync(string id)
|
||||||
@@ -96,13 +139,95 @@ public class IdentityService(
|
|||||||
|
|
||||||
if (response == null) return null;
|
if (response == null) return null;
|
||||||
|
|
||||||
var userModel = new UserModel()
|
var userModel = new UserModel
|
||||||
{
|
{
|
||||||
Id = response.Id,
|
Id = response.Id,
|
||||||
UserName = response.UserName,
|
UserName = response.UserName ?? string.Empty,
|
||||||
FirstName = response.FirstName,
|
FirstName = response.FirstName,
|
||||||
LastName = response.LastName,
|
LastName = response.LastName,
|
||||||
Email = response.Email,
|
Email = response.Email ?? string.Empty,
|
||||||
|
Occupation = response.Occupation,
|
||||||
|
PhoneNumber = 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
|
||||||
|
},
|
||||||
|
StoredDataUrls = new StoredDataUrlsModel
|
||||||
|
{
|
||||||
|
ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl,
|
||||||
|
BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl,
|
||||||
|
WebsiteIconUrl = response.StoredDataUrls.WebsiteIconUrl,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
PhoneNumber = 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
|
||||||
|
},
|
||||||
|
StoredDataUrls = new StoredDataUrlsModel
|
||||||
|
{
|
||||||
|
ProfilePictureUrl = response.StoredDataUrls.ProfilePictureUrl,
|
||||||
|
BannerPictureUrl = response.StoredDataUrls.BannerPictureUrl,
|
||||||
|
WebsiteIconUrl = response.StoredDataUrls.WebsiteIconUrl,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return userModel;
|
return userModel;
|
||||||
@@ -119,22 +244,49 @@ public class IdentityService(
|
|||||||
return await FindUserByIdAsync(currentUserId);
|
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
|
applicationUser.StoredDataUrls.BannerPictureUrl = url;
|
||||||
{
|
|
||||||
Id = response.Id,
|
var response = await userManager.UpdateAsync(applicationUser);
|
||||||
UserName = response.UserName,
|
|
||||||
FirstName = response.FirstName,
|
|
||||||
LastName = response.LastName,
|
|
||||||
Email = response.Email
|
|
||||||
};
|
|
||||||
|
|
||||||
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)
|
public async Task<bool> IsInRoleAsync(string userId, string role)
|
||||||
@@ -218,13 +370,22 @@ public class IdentityService(
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await GetUserByUserNameAsync(userName);
|
var user = await GetUserByUserNameAsync(userName);
|
||||||
|
|
||||||
|
if (user is null) throw new InvalidOperationException();
|
||||||
|
|
||||||
|
var jwtSection = configuration.GetRequiredSection("Authentication:Jwt");
|
||||||
|
|
||||||
var token = JwtTokenHelper.GenerateJwtToken(
|
var token = JwtTokenHelper.GenerateJwtToken(
|
||||||
issuer: configuration["Jwt-Issuer"] ?? "",
|
issuer: jwtSection["Issuer"] ?? "",
|
||||||
audience: configuration["Jwt-Audience"] ?? "",
|
audience: jwtSection["Audience"] ?? "",
|
||||||
key: configuration["Jwt-Key"] ?? "",
|
key: jwtSection["Key"] ?? "",
|
||||||
userId: user?.Id ?? "");
|
userId: user.Id,
|
||||||
|
email: user.Email,
|
||||||
|
firstname: user.FirstName,
|
||||||
|
lastname: user.LastName,
|
||||||
|
portraitUrl: user.ProfilePictureUrl);
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -2,12 +2,12 @@ namespace Hutopy.Infrastructure.Identity.OwnedEntities;
|
|||||||
|
|
||||||
public class SocialNetworks
|
public class SocialNetworks
|
||||||
{
|
{
|
||||||
public string FacebookUrl { get; init; } = String.Empty;
|
public string FacebookUrl { get; init; } = string.Empty;
|
||||||
public string InstagramUrl { get; init; } = String.Empty;
|
public string InstagramUrl { get; init; } = string.Empty;
|
||||||
public string XUrl { get; init; } = String.Empty;
|
public string XUrl { get; init; } = string.Empty;
|
||||||
public string LinkedInUrl { get; init; } = String.Empty;
|
public string LinkedInUrl { get; init; } = string.Empty;
|
||||||
public string TikTokUrl { get; init; } = String.Empty;
|
public string TikTokUrl { get; init; } = string.Empty;
|
||||||
public string YoutubeUrl { get; init; } = String.Empty;
|
public string YoutubeUrl { get; init; } = string.Empty;
|
||||||
public string RedditUrl { get; init; } = String.Empty;
|
public string RedditUrl { get; init; } = string.Empty;
|
||||||
public string YourWebsiteUrl { get; init; } = String.Empty;
|
public string YourWebsiteUrl { get; init; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
497
src/Infrastructure/Migrations/20240630001806_AddMissingInformationsToUser.Designer.cs
generated
Normal file
497
src/Infrastructure/Migrations/20240630001806_AddMissingInformationsToUser.Designer.cs
generated
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
// <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("20240630001806_AddMissingInformationsToUser")]
|
||||||
|
partial class AddMissingInformationsToUser
|
||||||
|
{
|
||||||
|
/// <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.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.Navigation("SocialNetworks")
|
||||||
|
.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Hutopy.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddMissingInformationsToUser : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "About",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Address",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "BirthDate",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "City",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Country",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Description",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Occupation",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "About",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Address",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "BirthDate",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "City",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Country",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Description",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Occupation",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
557
src/Infrastructure/Migrations/20240630163057_AddMoreInformationsToUser.Designer.cs
generated
Normal file
557
src/Infrastructure/Migrations/20240630163057_AddMoreInformationsToUser.Designer.cs
generated
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -149,13 +149,37 @@ namespace Hutopy.Infrastructure.Migrations
|
|||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
.HasColumnType("nvarchar(450)");
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("About")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<int>("AccessFailedCount")
|
b.Property<int>("AccessFailedCount")
|
||||||
.HasColumnType("int");
|
.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")
|
b.Property<string>("ConcurrencyStamp")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Country")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("Email")
|
b.Property<string>("Email")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
@@ -185,6 +209,10 @@ namespace Hutopy.Infrastructure.Migrations
|
|||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("Occupation")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("PasswordHash")
|
b.Property<string>("PasswordHash")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
@@ -361,6 +389,35 @@ namespace Hutopy.Infrastructure.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b =>
|
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 =>
|
b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.SocialNetworks", "SocialNetworks", b1 =>
|
||||||
{
|
{
|
||||||
b1.Property<string>("ApplicationUserId")
|
b1.Property<string>("ApplicationUserId")
|
||||||
@@ -406,8 +463,39 @@ namespace Hutopy.Infrastructure.Migrations
|
|||||||
.HasForeignKey("ApplicationUserId");
|
.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")
|
b.Navigation("SocialNetworks")
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("StoredDataUrls")
|
||||||
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Stripe;
|
using Stripe;
|
||||||
using Stripe.Checkout;
|
using Stripe.Checkout;
|
||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
|
|||||||
@@ -7,17 +7,26 @@ namespace Hutopy.Infrastructure.Utils;
|
|||||||
|
|
||||||
public static class JwtTokenHelper
|
public static class JwtTokenHelper
|
||||||
{
|
{
|
||||||
public static string GenerateJwtToken(string issuer, string audience, string key, string userId)
|
public static string GenerateJwtToken(string issuer, string audience, string key, string? userId, string? email,
|
||||||
|
string? firstname, string? lastname, string? portraitUrl)
|
||||||
{
|
{
|
||||||
var claims = new[]
|
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
|
||||||
|
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
|
||||||
|
|
||||||
|
var claims = new List<Claim>(new[]
|
||||||
{
|
{
|
||||||
new Claim(JwtRegisteredClaimNames.Sub, userId),
|
new Claim(JwtRegisteredClaimNames.Sub, userId),
|
||||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||||
new Claim(ClaimTypes.NameIdentifier, userId)
|
new Claim(ClaimTypes.NameIdentifier, userId),
|
||||||
};
|
new Claim(ClaimTypes.Email, email),
|
||||||
|
new Claim(ClaimTypes.GivenName, firstname),
|
||||||
|
new Claim(ClaimTypes.Surname, lastname),
|
||||||
|
});
|
||||||
|
|
||||||
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
|
if (portraitUrl is not null)
|
||||||
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
|
{
|
||||||
|
claims.Add(new Claim("portrait-url", portraitUrl));
|
||||||
|
}
|
||||||
|
|
||||||
var token = new JwtSecurityToken(
|
var token = new JwtSecurityToken(
|
||||||
issuer: issuer,
|
issuer: issuer,
|
||||||
|
|||||||
@@ -13,41 +13,45 @@ public class FacebookController(IIdentityService identityService) : Controller
|
|||||||
[HttpGet("/api/facebook/sign-in")]
|
[HttpGet("/api/facebook/sign-in")]
|
||||||
public async Task SignIn()
|
public async Task SignIn()
|
||||||
{
|
{
|
||||||
await HttpContext.ChallengeAsync(FacebookDefaults.AuthenticationScheme, new AuthenticationProperties
|
await HttpContext.ChallengeAsync(FacebookDefaults.AuthenticationScheme,
|
||||||
{
|
new AuthenticationProperties { RedirectUri = Url.Action("Authorize") });
|
||||||
RedirectUri = Url.Action("Authorize")
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> Authorize()
|
public async Task<IActionResult> Authorize()
|
||||||
{
|
{
|
||||||
var authenticateResult = await HttpContext.AuthenticateAsync(FacebookDefaults.AuthenticationScheme);
|
var authenticateResult = await HttpContext.AuthenticateAsync(FacebookDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
if (!authenticateResult.Succeeded) return BadRequest();
|
if (!authenticateResult.Succeeded) return BadRequest();
|
||||||
|
|
||||||
var claims = authenticateResult.Principal.Claims.ToList();
|
var claims = authenticateResult.Principal.Claims.ToList();
|
||||||
|
|
||||||
var name = claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value ?? "";
|
var name = claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value ?? "";
|
||||||
var email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value ?? "";
|
var email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value ?? "";
|
||||||
var givenName = claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value ?? "";
|
var givenName = claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value ?? "";
|
||||||
var familyName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value ?? "";
|
var familyName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value ?? "";
|
||||||
|
|
||||||
var claimsIdentity = new ClaimsIdentity(new List<Claim>
|
var claimsIdentity = new ClaimsIdentity(
|
||||||
{
|
new List<Claim>
|
||||||
new(ClaimTypes.Name, name),
|
{
|
||||||
new(ClaimTypes.Email, email),
|
new(ClaimTypes.Name, name),
|
||||||
new(ClaimTypes.GivenName, givenName),
|
new(ClaimTypes.Email, email),
|
||||||
new(ClaimTypes.Surname, familyName)
|
new(ClaimTypes.GivenName, givenName),
|
||||||
}, CookieAuthenticationDefaults.AuthenticationScheme);
|
new(ClaimTypes.Surname, familyName)
|
||||||
|
},
|
||||||
|
CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
if (await identityService.FindUserByEmailAsync(email) != null)
|
if (await identityService.FindUserByEmailAsync(email) != null)
|
||||||
{
|
{
|
||||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
await HttpContext.SignInAsync(
|
||||||
|
CookieAuthenticationDefaults.AuthenticationScheme,
|
||||||
|
new ClaimsPrincipal(claimsIdentity));
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
await identityService.CreateUserAsync(email, givenName, givenName, familyName, RandomGenerator.RandomString(24));
|
await identityService.CreateUserAsync(email, givenName, givenName, familyName,
|
||||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
RandomGenerator.RandomString(24));
|
||||||
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
|
||||||
|
new ClaimsPrincipal(claimsIdentity));
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ public class GoogleController(IIdentityService identityService, IHttpClientFacto
|
|||||||
[HttpPost("/api/google/sign-in")]
|
[HttpPost("/api/google/sign-in")]
|
||||||
public async Task<IActionResult> SignIn([FromBody] GoogleSignInRequest request)
|
public async Task<IActionResult> SignIn([FromBody] GoogleSignInRequest request)
|
||||||
{
|
{
|
||||||
var httpClient = httpClientFactory.CreateClient();
|
using var httpClient = httpClientFactory.CreateClient();
|
||||||
|
|
||||||
// Verify the token with Google
|
// Verify the token with Google
|
||||||
var response = await httpClient.GetAsync($"https://www.googleapis.com/oauth2/v1/userinfo?access_token={request.AccessToken}");
|
var response = await httpClient.GetAsync($"https://www.googleapis.com/oauth2/v1/userinfo?access_token={request.AccessToken}");
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
@@ -21,12 +22,11 @@ public class GoogleController(IIdentityService identityService, IHttpClientFacto
|
|||||||
return BadRequest("Invalid Google token.");
|
return BadRequest("Invalid Google token.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
|
var userInfo = JObject.Parse(await response.Content.ReadAsStringAsync());
|
||||||
|
var email = userInfo["email"]?.ToString() ?? "";
|
||||||
var email = payload["email"]?.ToString() ?? "";
|
var name = userInfo["name"]?.ToString() ?? "";
|
||||||
var name = payload["name"]?.ToString() ?? "";
|
var givenName = userInfo["given_name"]?.ToString() ?? "";
|
||||||
var givenName = payload["given_name"]?.ToString() ?? "";
|
var familyName = userInfo["family_name"]?.ToString() ?? "";
|
||||||
var familyName = payload["family_name"]?.ToString() ?? "";
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(email))
|
if (string.IsNullOrEmpty(email))
|
||||||
{
|
{
|
||||||
@@ -47,27 +47,33 @@ public class GoogleController(IIdentityService identityService, IHttpClientFacto
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sign in the user
|
// Sign in the user
|
||||||
var claims = new List<Claim>
|
var claimsIdentity = new ClaimsIdentity(
|
||||||
{
|
new List<Claim>
|
||||||
new(ClaimTypes.Name, name),
|
{
|
||||||
new(ClaimTypes.Email, email),
|
new(ClaimTypes.Name, name),
|
||||||
new(ClaimTypes.GivenName, givenName),
|
new(ClaimTypes.Email, email),
|
||||||
new(ClaimTypes.Surname, familyName)
|
new(ClaimTypes.GivenName, givenName),
|
||||||
};
|
new(ClaimTypes.Surname, familyName)
|
||||||
|
},
|
||||||
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
|
CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
|
|
||||||
|
|
||||||
var issuer = configuration["Jwt-Issuer"] ??
|
|
||||||
throw new ArgumentNullException("The Jwt issuer is missing.");
|
|
||||||
var audience = configuration["Jwt-Audience"] ??
|
|
||||||
throw new ArgumentNullException("The Jwt audience is missing.");
|
|
||||||
var key = configuration["Jwt-Key"] ??
|
|
||||||
throw new ArgumentNullException("The Jwt key is missing.");
|
|
||||||
|
|
||||||
var jwtToken = JwtTokenHelper.GenerateJwtToken(issuer, audience, key, user.Id);
|
|
||||||
|
|
||||||
return Ok(new { accessToken = jwtToken, email });
|
await HttpContext.SignInAsync(
|
||||||
|
CookieAuthenticationDefaults.AuthenticationScheme,
|
||||||
|
new ClaimsPrincipal(claimsIdentity));
|
||||||
|
|
||||||
|
var jwtSection = configuration.GetRequiredSection("Authentication:Jwt");
|
||||||
|
|
||||||
|
var token = JwtTokenHelper.GenerateJwtToken(
|
||||||
|
jwtSection["Issuer"] ?? throw new ArgumentNullException("The Jwt issuer is missing."),
|
||||||
|
jwtSection["Audience"] ?? throw new ArgumentNullException("The Jwt audience is missing."),
|
||||||
|
jwtSection["Key"] ?? throw new ArgumentNullException("The Jwt key is missing."),
|
||||||
|
user.Id,
|
||||||
|
user.Email,
|
||||||
|
user.FirstName,
|
||||||
|
user.LastName,
|
||||||
|
user.ProfilePictureUrl);
|
||||||
|
|
||||||
|
return Ok(new { accessToken = token, email });
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GoogleSignInRequest
|
public class GoogleSignInRequest
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public static class DependencyInjection
|
|||||||
services.AddExceptionHandler<CustomExceptionHandler>();
|
services.AddExceptionHandler<CustomExceptionHandler>();
|
||||||
|
|
||||||
services.AddRazorPages();
|
services.AddRazorPages();
|
||||||
|
|
||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
|
|
||||||
// Customise default API behaviour
|
// Customise default API behaviour
|
||||||
@@ -39,26 +39,11 @@ public static class DependencyInjection
|
|||||||
|
|
||||||
services.AddEndpointsApiExplorer();
|
services.AddEndpointsApiExplorer();
|
||||||
|
|
||||||
services.AddOpenApiDocument((configure, sp) =>
|
|
||||||
{
|
|
||||||
configure.Title = "Hutopy API";
|
|
||||||
|
|
||||||
// Add JWT
|
|
||||||
configure.AddSecurity("JWT", Enumerable.Empty<string>(), new OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Type = OpenApiSecuritySchemeType.ApiKey,
|
|
||||||
Name = "Authorization",
|
|
||||||
In = OpenApiSecurityApiKeyLocation.Header,
|
|
||||||
Description = "Type into the textbox: Bearer {your JWT token}."
|
|
||||||
});
|
|
||||||
|
|
||||||
configure.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("JWT"));
|
|
||||||
});
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddKeyVaultIfConfigured(this IServiceCollection services, ConfigurationManager configuration)
|
public static IServiceCollection AddKeyVaultIfConfigured(this IServiceCollection services,
|
||||||
|
ConfigurationManager configuration)
|
||||||
{
|
{
|
||||||
var keyVaultUri = configuration["KeyVaultUri"];
|
var keyVaultUri = configuration["KeyVaultUri"];
|
||||||
if (!string.IsNullOrWhiteSpace(keyVaultUri))
|
if (!string.IsNullOrWhiteSpace(keyVaultUri))
|
||||||
@@ -70,10 +55,12 @@ public static class DependencyInjection
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddAuthorizationAndAuthentication(this IServiceCollection services, ConfigurationManager configuration)
|
public static IServiceCollection AddAuthorizationAndAuthentication(this IServiceCollection services,
|
||||||
|
ConfigurationManager configuration)
|
||||||
{
|
{
|
||||||
services.AddAuthentication(options =>
|
var authenticationBuilder = services
|
||||||
|
.AddAuthentication(options =>
|
||||||
{
|
{
|
||||||
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
||||||
@@ -81,36 +68,50 @@ public static class DependencyInjection
|
|||||||
.AddCookie("Identity.Application", options =>
|
.AddCookie("Identity.Application", options =>
|
||||||
{
|
{
|
||||||
options.LoginPath = "/api/Users/login";
|
options.LoginPath = "/api/Users/login";
|
||||||
})
|
});
|
||||||
.AddCookie()
|
|
||||||
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtBearerOptions =>
|
var authJwt = configuration.GetSection("Authentication:Jwt");
|
||||||
|
if (authJwt.Exists())
|
||||||
|
{
|
||||||
|
authenticationBuilder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtBearerOptions =>
|
||||||
{
|
{
|
||||||
jwtBearerOptions.Authority = "https://hutopy.com";
|
jwtBearerOptions.Authority = "https://hutopy.com";
|
||||||
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
|
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
|
||||||
{
|
{
|
||||||
ValidateIssuer = true,
|
ValidateIssuer = true,
|
||||||
ValidIssuer = configuration["Jwt-Issuer"],
|
ValidIssuer = authJwt["Issuer"],
|
||||||
ValidateAudience = true,
|
ValidateAudience = true,
|
||||||
ValidAudience = configuration["Jwt-Audience"],
|
ValidAudience = authJwt["Audience"],
|
||||||
ValidateLifetime = true,
|
ValidateLifetime = true,
|
||||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt-Key"] ??
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authJwt["Key"] ??
|
||||||
throw new ArgumentNullException("The Jwt Key is missing.")))
|
throw new ArgumentNullException("The Jwt Key is missing.")))
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
.AddGoogle(GoogleDefaults.AuthenticationScheme, options =>
|
}
|
||||||
|
|
||||||
|
var authGoogle = configuration.GetSection("Authentication:Google");
|
||||||
|
if (authGoogle.Exists())
|
||||||
|
{
|
||||||
|
authenticationBuilder.AddGoogle(GoogleDefaults.AuthenticationScheme, options =>
|
||||||
{
|
{
|
||||||
options.ClientId = configuration["Google-ClientId"] ??
|
options.ClientId = authGoogle["ClientId"] ??
|
||||||
throw new ArgumentNullException("The Google ClientId is missing.");;
|
throw new ArgumentNullException("The Google ClientId is missing.");
|
||||||
options.ClientSecret = configuration["Google-ClientSecret"] ??
|
options.ClientSecret = authGoogle["ClientSecret"] ??
|
||||||
throw new ArgumentNullException("The Google ClientSecret is missing.");;
|
throw new ArgumentNullException("The Google ClientSecret is missing.");
|
||||||
})
|
});
|
||||||
.AddFacebook(FacebookDefaults.AuthenticationScheme, options =>
|
}
|
||||||
|
|
||||||
|
var authFacebook = configuration.GetSection("Authentication:Facebook");
|
||||||
|
if (authFacebook.Exists())
|
||||||
|
{
|
||||||
|
authenticationBuilder.AddFacebook(FacebookDefaults.AuthenticationScheme, options =>
|
||||||
{
|
{
|
||||||
options.ClientId = configuration["Facebook-ClientId"] ??
|
options.ClientId = authFacebook["ClientId"] ??
|
||||||
throw new ArgumentNullException("The Facebook ClientId is missing.");
|
throw new ArgumentNullException("The Facebook ClientId is missing.");
|
||||||
options.ClientSecret = configuration["Facebook-ClientSecret"] ??
|
options.ClientSecret = authFacebook["ClientSecret"] ??
|
||||||
throw new ArgumentNullException("The Facebook ClientSecret is missing.");
|
throw new ArgumentNullException("The Facebook ClientSecret is missing.");
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/Web/Endpoints/UpdateMyUser.cs
Normal file
39
src/Web/Endpoints/UpdateMyUser.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Hutopy.Application.Users.Commands;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Endpoints;
|
||||||
|
|
||||||
|
public class UpdateMyUser : EndpointGroupBase
|
||||||
|
{
|
||||||
|
public override void Map(WebApplication app)
|
||||||
|
{
|
||||||
|
app.MapGroup(this)
|
||||||
|
.RequireAuthorization()
|
||||||
|
.MapPost(UpdateCurrentUserProfilePicture, "/profile-picture")
|
||||||
|
.MapPost(UpdateCurrentUserBannerPicture, "/banner-picture")
|
||||||
|
.MapPost(UpdateCurrentUserWebsiteIcon, "/website-icon")
|
||||||
|
.MapPatch("/profile", UpdateCurrentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<string> UpdateCurrentUser(ISender sender, UpdateCurrentUserCommand command)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,18 +23,16 @@ public class Users : EndpointGroupBase
|
|||||||
{
|
{
|
||||||
return await sender.Send(query);
|
return await sender.Send(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> Login(ISender sender, LoginCommand command)
|
private static async Task<LoginResponse> Login(ISender sender, LoginCommand command)
|
||||||
{
|
{
|
||||||
return await sender.Send(command);
|
return await sender.Send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> UploadProfilePicture(ISender sender, Stream stream)
|
private static async Task<string> UploadProfilePicture(ISender sender, Stream stream)
|
||||||
{
|
{
|
||||||
var command = new UploadProfilePictureCommand
|
var command = new UploadProfilePictureCommand { ProfilePicture = stream };
|
||||||
{
|
|
||||||
ProfilePicture = stream
|
|
||||||
};
|
|
||||||
return await sender.Send(command);
|
return await sender.Send(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/Web/Messages/Data/Message.cs
Normal file
13
src/Web/Messages/Data/Message.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Hutopy.Web.Messages.Data;
|
||||||
|
|
||||||
|
public class Message
|
||||||
|
{
|
||||||
|
public Guid Id { get; init; }
|
||||||
|
public Guid ContentId { get; init; } // works for any - VideoId, ChatId, RoomId, xxxId, ForumId
|
||||||
|
public Guid CreatedBy { get; init; }
|
||||||
|
public DateTime CreatedAt { get; }
|
||||||
|
|
||||||
|
public Guid ParentId { get; init; }
|
||||||
|
|
||||||
|
public string Value { get; init; } = null!;
|
||||||
|
}
|
||||||
19
src/Web/Messages/Data/MessagingDbContext.cs
Normal file
19
src/Web/Messages/Data/MessagingDbContext.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Messages.Data;
|
||||||
|
|
||||||
|
public class MessagingDbContext(
|
||||||
|
DbContextOptions<MessagingDbContext> options)
|
||||||
|
: DbContext(options)
|
||||||
|
{
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder
|
||||||
|
.Entity<Message>()
|
||||||
|
.Property(c => c.CreatedAt)
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<Message> Messages { get; set; }
|
||||||
|
}
|
||||||
30
src/Web/Messages/Handlers/GetMessages.cs
Normal file
30
src/Web/Messages/Handlers/GetMessages.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using Hutopy.Web.Messages.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Messages.Handlers;
|
||||||
|
|
||||||
|
public class GetMessages(
|
||||||
|
MessagingDbContext context)
|
||||||
|
: EndpointWithoutRequest<List<Message>>
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Tags("Messages");
|
||||||
|
Get("/api/messages/{ContentId:guid}");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(
|
||||||
|
CancellationToken ct)
|
||||||
|
{
|
||||||
|
var contentId = Route<Guid>("ContentId");
|
||||||
|
|
||||||
|
var comments = await context
|
||||||
|
.Messages
|
||||||
|
.Where(c => c.ContentId == contentId)
|
||||||
|
.ToListAsync(cancellationToken: ct);
|
||||||
|
|
||||||
|
await SendAsync(comments, cancellation: ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/Web/Messages/Handlers/GetMessagesByUser.cs
Normal file
33
src/Web/Messages/Handlers/GetMessagesByUser.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using Hutopy.Web.Messages.Data;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Messages.Handlers;
|
||||||
|
|
||||||
|
public record GetMessagesByUserRequest(
|
||||||
|
[FromRoute] Guid UserId);
|
||||||
|
|
||||||
|
public class GetMessagesByUser(
|
||||||
|
MessagingDbContext context)
|
||||||
|
: EndpointWithoutRequest<List<Message>>
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Tags("Messages");
|
||||||
|
Get("/api/messages/by-user/{UserId:guid}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(
|
||||||
|
CancellationToken ct)
|
||||||
|
{
|
||||||
|
var userId = Route<Guid>("UserId");
|
||||||
|
|
||||||
|
var posts = await context
|
||||||
|
.Messages
|
||||||
|
.Where(c => c.CreatedBy == userId)
|
||||||
|
.ToListAsync(cancellationToken: ct);
|
||||||
|
|
||||||
|
await SendAsync(posts, cancellation: ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/Web/Messages/Handlers/PostMessage.cs
Normal file
34
src/Web/Messages/Handlers/PostMessage.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using Hutopy.Web.Messages.Data;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Messages.Handlers;
|
||||||
|
|
||||||
|
public record PostMessageRequest(
|
||||||
|
Guid ContentId,
|
||||||
|
string Message);
|
||||||
|
|
||||||
|
public class PostMessage(
|
||||||
|
MessagingDbContext context)
|
||||||
|
: Endpoint<PostMessageRequest>
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
// TODO: Find how to specify the name we see in Swagger
|
||||||
|
Tags("Messages");
|
||||||
|
Post("/api/messages");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(
|
||||||
|
PostMessageRequest req,
|
||||||
|
CancellationToken ct)
|
||||||
|
{
|
||||||
|
await context.Messages.AddAsync(
|
||||||
|
new Message {
|
||||||
|
ContentId = req.ContentId,
|
||||||
|
CreatedBy = User.GetUserId(),
|
||||||
|
Value = req.Message },
|
||||||
|
ct);
|
||||||
|
|
||||||
|
await context.SaveChangesAsync(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/Web/Messages/Handlers/PostReplyMessage.cs
Normal file
37
src/Web/Messages/Handlers/PostReplyMessage.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using FastEndpoints;
|
||||||
|
using Hutopy.Web.Messages.Data;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Messages.Handlers;
|
||||||
|
|
||||||
|
public record PostReplyMessageRequest(
|
||||||
|
Guid ContentId,
|
||||||
|
Guid ParentId,
|
||||||
|
string Message);
|
||||||
|
|
||||||
|
public sealed class PostReplyMessage(
|
||||||
|
MessagingDbContext context)
|
||||||
|
: Endpoint<PostReplyMessageRequest>
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Tags("Messages");
|
||||||
|
Post("/api/messages/reply");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(
|
||||||
|
PostReplyMessageRequest req,
|
||||||
|
CancellationToken ct)
|
||||||
|
{
|
||||||
|
await context.Messages.AddAsync(
|
||||||
|
new Message
|
||||||
|
{
|
||||||
|
ContentId = req.ContentId,
|
||||||
|
ParentId = req.ParentId,
|
||||||
|
CreatedBy = User.GetUserId(),
|
||||||
|
Value = req.Message
|
||||||
|
},
|
||||||
|
ct);
|
||||||
|
|
||||||
|
await context.SaveChangesAsync(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/Web/Messages/Migrations/20240627081653_Initial.Designer.cs
generated
Normal file
59
src/Web/Messages/Migrations/20240627081653_Initial.Designer.cs
generated
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Hutopy.Web.Messages.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.Web.Messages.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MessagingDbContext))]
|
||||||
|
[Migration("20240627081653_Initial")]
|
||||||
|
partial class Initial
|
||||||
|
{
|
||||||
|
/// <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.Web.Messages.Data.Message", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("ContentId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("ParentId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Messages");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/Web/Messages/Migrations/20240627081653_Initial.cs
Normal file
38
src/Web/Messages/Migrations/20240627081653_Initial.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Messages.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Initial : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Messages",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
ContentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
CreatedBy = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"),
|
||||||
|
ParentId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
|
Value = table.Column<string>(type: "nvarchar(max)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Messages", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Messages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Hutopy.Web.Messages.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Messages.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MessagingDbContext))]
|
||||||
|
partial class MessagingDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.3")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Hutopy.Web.Messages.Data.Message", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("ContentId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatedBy")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("ParentId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Messages");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/Web/Messages/Shared.cs
Normal file
42
src/Web/Messages/Shared.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Hutopy.Web.Messages;
|
||||||
|
|
||||||
|
public class Shared(string claimName) : Exception;
|
||||||
|
|
||||||
|
public static class ClaimsPrincipalExtensions
|
||||||
|
{
|
||||||
|
public static Guid GetUserId(this ClaimsPrincipal claims)
|
||||||
|
{
|
||||||
|
return (Guid)claims.GetFirstValue<Guid>(ClaimTypes.NameIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetFirstName(this ClaimsPrincipal claims)
|
||||||
|
{
|
||||||
|
return (string)claims.GetFirstValue<string>(ClaimTypes.GivenName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetLastName(this ClaimsPrincipal claims)
|
||||||
|
{
|
||||||
|
return (string)claims.GetFirstValue<string>(ClaimTypes.Surname);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetEmail(this ClaimsPrincipal claims)
|
||||||
|
{
|
||||||
|
return (string)claims.GetFirstValue<string>(ClaimTypes.Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object GetFirstValue<TValue>(this ClaimsPrincipal claims, string key)
|
||||||
|
{
|
||||||
|
var claim = claims.FindFirst(key);
|
||||||
|
|
||||||
|
if (claim is null) throw new Shared(key);
|
||||||
|
|
||||||
|
if (typeof(TValue) == typeof(Guid))
|
||||||
|
{
|
||||||
|
return Guid.Parse(claim.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.ChangeType(claim.Value, typeof(TValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
|
using Azure.Identity;
|
||||||
|
using FastEndpoints;
|
||||||
using Hutopy.Application;
|
using Hutopy.Application;
|
||||||
using Hutopy.Infrastructure;
|
using Hutopy.Infrastructure;
|
||||||
using Hutopy.Infrastructure.Data;
|
using Hutopy.Infrastructure.Data;
|
||||||
using Hutopy.Web;
|
using Hutopy.Web;
|
||||||
using Azure.Identity;
|
using Hutopy.Web.Messages.Data;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NSwag;
|
||||||
|
using NSwag.Generation.AspNetCore.Processors;
|
||||||
|
using NSwag.Generation.Processors.Security;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -14,31 +20,31 @@ if (!builder.Environment.IsDevelopment())
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("AllowAll", builder =>
|
||||||
{
|
{
|
||||||
options.AddPolicy("AllowAll", builder =>
|
builder.AllowAnyOrigin()
|
||||||
{
|
.AllowAnyMethod()
|
||||||
builder.AllowAnyOrigin()
|
.AllowAnyHeader();
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader();
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddPolicy("AllowHutopyUi", builder =>
|
|
||||||
{
|
|
||||||
builder.WithOrigins("https://zealous-bay-08204590f.5.azurestaticapps.net")
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowCredentials();
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddPolicy("AllowHutopyUiPreview", builder =>
|
|
||||||
{
|
|
||||||
builder.WithOrigins("https://zealous-bay-08204590f-preview.eastus2.5.azurestaticapps.net")
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowCredentials();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
options.AddPolicy("AllowHutopyUi", builder =>
|
||||||
|
{
|
||||||
|
builder.WithOrigins("https://zealous-bay-08204590f.5.azurestaticapps.net")
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowCredentials();
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddPolicy("AllowHutopyUiPreview", builder =>
|
||||||
|
{
|
||||||
|
builder.WithOrigins("https://zealous-bay-08204590f-preview.eastus2.5.azurestaticapps.net")
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowCredentials();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddKeyVaultIfConfigured(builder.Configuration);
|
builder.Services.AddKeyVaultIfConfigured(builder.Configuration);
|
||||||
|
|
||||||
@@ -46,8 +52,37 @@ builder.Services.AddApplicationServices();
|
|||||||
builder.Services.AddInfrastructureServices(builder.Configuration);
|
builder.Services.AddInfrastructureServices(builder.Configuration);
|
||||||
builder.Services.AddWebServices();
|
builder.Services.AddWebServices();
|
||||||
builder.Services.AddAuthorizationAndAuthentication(builder.Configuration);
|
builder.Services.AddAuthorizationAndAuthentication(builder.Configuration);
|
||||||
|
|
||||||
|
// TODO: This old tech should be remove - need to move Facebook / Google controllers to FastEndpoints
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
|
builder.Services.AddOpenApiDocument((configure, sp) =>
|
||||||
|
{
|
||||||
|
configure.Title = "Hutopy API";
|
||||||
|
|
||||||
|
// Add JWT
|
||||||
|
configure.AddSecurity(
|
||||||
|
"JWT",
|
||||||
|
[],
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Type = OpenApiSecuritySchemeType.ApiKey,
|
||||||
|
Name = "Authorization",
|
||||||
|
In = OpenApiSecurityApiKeyLocation.Header,
|
||||||
|
Description = "Type into the textbox: Bearer {your JWT token}.",
|
||||||
|
});
|
||||||
|
|
||||||
|
configure.OperationProcessors.Add(new AspNetCoreOperationTagsProcessor());
|
||||||
|
configure.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("JWT"));
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddFastEndpoints();
|
||||||
|
|
||||||
|
builder.Services.AddDbContext<MessagingDbContext>((_, options) =>
|
||||||
|
{
|
||||||
|
options.UseSqlServer(builder.Configuration.GetConnectionString("CommentStore"));
|
||||||
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseForwardedHeaders(
|
app.UseForwardedHeaders(
|
||||||
@@ -75,24 +110,27 @@ app.UseHealthChecks("/health");
|
|||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseSwaggerUi(settings =>
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
settings.Path = "/api";
|
app.UseOpenApi();
|
||||||
settings.DocumentPath = "/api/specification.json";
|
app.UseSwaggerUi(options => options.Path = "/api");
|
||||||
});
|
}
|
||||||
|
|
||||||
app.MapControllerRoute(
|
app.MapControllerRoute(
|
||||||
name: "default",
|
name: "default",
|
||||||
pattern: "{controller}/{action=Index}/{id?}");
|
pattern: "{controller}/{action=Index}/{id?}");
|
||||||
|
|
||||||
app.MapFallbackToFile("index.html");
|
//TODO: validate the behavior
|
||||||
|
// app.UseExceptionHandler();
|
||||||
app.UseExceptionHandler(options => { });
|
|
||||||
|
|
||||||
app.Map("/", () => Results.Redirect("/api"));
|
|
||||||
|
|
||||||
app.MapEndpoints();
|
app.MapEndpoints();
|
||||||
|
|
||||||
|
app.UseFastEndpoints();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
public abstract partial class Program { }
|
namespace Hutopy.Web
|
||||||
|
{
|
||||||
|
public abstract partial class Program
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"Hutopy.Web": {
|
"Hutopy.Web - DEV": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": false,
|
||||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
"IIS Express": {
|
"IIS Express": {
|
||||||
"commandName": "IISExpress",
|
"commandName": "IISExpress",
|
||||||
"launchBrowser": true,
|
"launchBrowser": false,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" />
|
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" />
|
||||||
<PackageReference Include="Azure.Identity" />
|
<PackageReference Include="Azure.Identity" />
|
||||||
|
<PackageReference Include="FastEndpoints" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||||
@@ -24,29 +25,13 @@
|
|||||||
<PackageReference Include="NSwag.AspNetCore" />
|
<PackageReference Include="NSwag.AspNetCore" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="NSwag.MSBuild">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="FluentValidation.AspNetCore" />
|
<PackageReference Include="FluentValidation.AspNetCore" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
<!-- Auto-generated Open API specification and Angular TypeScript clients -->
|
<Folder Include="Messages\Migrations\" />
|
||||||
<PropertyGroup>
|
</ItemGroup>
|
||||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<Target Name="NSwag" AfterTargets="PostBuildEvent" Condition=" '$(Configuration)' == 'Debug' And '$(SkipNSwag)' != 'True' ">
|
|
||||||
<Exec ConsoleToMSBuild="true" ContinueOnError="true" WorkingDirectory="$(ProjectDir)" EnvironmentVariables="ASPNETCORE_ENVIRONMENT=Development" Command="$(NSwagExe_Net80) run config.nswag /variables:Configuration=$(Configuration)">
|
|
||||||
<Output TaskParameter="ExitCode" PropertyName="NSwagExitCode" />
|
|
||||||
<Output TaskParameter="ConsoleOutput" PropertyName="NSwagOutput" />
|
|
||||||
</Exec>
|
|
||||||
|
|
||||||
<Message Text="$(NSwagOutput)" Condition="'$(NSwagExitCode)' == '0'" Importance="low" />
|
|
||||||
<Error Text="$(NSwagOutput)" Condition="'$(NSwagExitCode)' != '0'" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
112
src/Web/Web.http
112
src/Web/Web.http
@@ -1,12 +1,10 @@
|
|||||||
# For more info on HTTP files go to https://aka.ms/vs/httpfile
|
# For more info on HTTP files go to https://aka.ms/vs/httpfile
|
||||||
@Web_HostAddress = https://localhost:5001
|
|
||||||
|
|
||||||
@Email=administrator@localhost
|
@Email=administrator@localhost
|
||||||
@Password=Administrator1!
|
@Password=Administrator1!
|
||||||
@BearerToken=<YourToken>
|
@auth_token=<Your Token>
|
||||||
|
|
||||||
# POST Users Register
|
# POST Users Register
|
||||||
POST {{Web_HostAddress}}/api/Users/Register
|
POST {{base_url}}/api/Users/Register
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -17,7 +15,7 @@ Content-Type: application/json
|
|||||||
###
|
###
|
||||||
|
|
||||||
# POST Users Login
|
# POST Users Login
|
||||||
POST {{Web_HostAddress}}/api/Users/Login
|
POST {{base_url}}/api/Users/login
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -25,11 +23,13 @@ Content-Type: application/json
|
|||||||
"password": "{{Password}}"
|
"password": "{{Password}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> {% client.global.set("auth_token", response.body.accessToken); %}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# POST Users Refresh
|
# POST Users Refresh
|
||||||
POST {{Web_HostAddress}}/api/Users/Refresh
|
POST {{base_url}}/api/Users/Refresh
|
||||||
Authorization: Bearer {{BearerToken}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -39,101 +39,23 @@ Content-Type: application/json
|
|||||||
###
|
###
|
||||||
|
|
||||||
# GET WeatherForecast
|
# GET WeatherForecast
|
||||||
GET {{Web_HostAddress}}/api/WeatherForecasts
|
GET {{base_url}}/api/WeatherForecasts
|
||||||
Authorization: Bearer {{BearerToken}}
|
Authorization: Bearer {{auth_token}}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# GET TodoLists
|
# GET GetMyUser
|
||||||
GET {{Web_HostAddress}}/api/TodoLists
|
GET {{base_url}}/api/GetMyUser
|
||||||
Authorization: Bearer {{BearerToken}}
|
Authorization: Bearer {{auth_token}}
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# POST TodoLists
|
# GET /api/posts
|
||||||
POST {{Web_HostAddress}}/api/TodoLists
|
|
||||||
Authorization: Bearer {{BearerToken}}
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
// CreateTodoListCommand
|
GET {{base_url}}/api/messages/00000001-0000-0000-0000-000000000001
|
||||||
{
|
|
||||||
"Title": "Backlog"
|
|
||||||
}
|
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
# PUT TodoLists
|
# GET /api/posts/by-user
|
||||||
PUT {{Web_HostAddress}}/api/TodoLists/1
|
GET {{base_url}}/api/messages/by-user/325C69E8-DBC4-4CEE-B56E-C3C90AEE7963
|
||||||
Authorization: Bearer {{BearerToken}}
|
Authorization: Bearer {{auth_token}}
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
// UpdateTodoListCommand
|
|
||||||
{
|
|
||||||
"Id": 1,
|
|
||||||
"Title": "Product Backlog"
|
|
||||||
}
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
# DELETE TodoLists
|
|
||||||
DELETE {{Web_HostAddress}}/api/TodoLists/1
|
|
||||||
Authorization: Bearer {{BearerToken}}
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
# GET TodoItems
|
|
||||||
@PageNumber = 1
|
|
||||||
@PageSize = 10
|
|
||||||
GET {{Web_HostAddress}}/api/TodoItems?ListId=1&PageNumber={{PageNumber}}&PageSize={{PageSize}}
|
|
||||||
|
|
||||||
Authorization: Bearer {{BearerToken}}
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
# POST TodoItems
|
|
||||||
POST {{Web_HostAddress}}/api/TodoItems
|
|
||||||
Authorization: Bearer {{BearerToken}}
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
// CreateTodoItemCommand
|
|
||||||
{
|
|
||||||
"ListId": 1,
|
|
||||||
"Title": "Eat a burrito 🌯"
|
|
||||||
}
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
#PUT TodoItems UpdateItemDetails
|
|
||||||
PUT {{Web_HostAddress}}/api/TodoItems/UpdateItemDetails?Id=1
|
|
||||||
Authorization: Bearer {{BearerToken}}
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
// UpdateTodoItemDetailCommand
|
|
||||||
{
|
|
||||||
"Id": 1,
|
|
||||||
"ListId": 1,
|
|
||||||
"Priority": 3,
|
|
||||||
"Note": "This is a good idea!"
|
|
||||||
}
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
# PUT TodoItems
|
|
||||||
PUT {{Web_HostAddress}}/api/TodoItems/1
|
|
||||||
Authorization: Bearer {{BearerToken}}
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
// UpdateTodoItemCommand
|
|
||||||
{
|
|
||||||
"Id": 1,
|
|
||||||
"Title": "Eat a yummy burrito 🌯",
|
|
||||||
"Done": true
|
|
||||||
}
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
# DELETE TodoItem
|
|
||||||
DELETE {{Web_HostAddress}}/api/TodoItems/1
|
|
||||||
Authorization: Bearer {{BearerToken}}
|
|
||||||
|
|
||||||
###
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft": "Information",
|
|
||||||
"Microsoft.AspNetCore.SpaProxy": "Information",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Google-ClientId": "",
|
|
||||||
"Google-ClientSecret": "",
|
|
||||||
"Facebook-ClientId": "",
|
|
||||||
"Facebook-ClientSecret": "",
|
|
||||||
"Jwt-Audience": "",
|
|
||||||
"Jwt-Issuer": "",
|
|
||||||
"Jwt-Key": "",
|
|
||||||
}
|
|
||||||
21
src/Web/appsettings.Development.json
Normal file
21
src/Web/appsettings.Development.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Information",
|
||||||
|
"Microsoft.AspNetCore.SpaProxy": "Information",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"DefaultConnection": "Server=localhost,1433;Initial Catalog=Hutopy;User Id=sa;Password=P@ssword123!;MultipleActiveResultSets=true;TrustServerCertificate=True;MultiSubnetFailover=True",
|
||||||
|
"CommentStore": "Server=localhost,1433;Initial Catalog=Hutopy;User Id=sa;Password=P@ssword123!;MultipleActiveResultSets=true;TrustServerCertificate=True;MultiSubnetFailover=True"
|
||||||
|
},
|
||||||
|
"Authentication": {
|
||||||
|
"Jwt": {
|
||||||
|
"Audience": "hutopy",
|
||||||
|
"Issuer": "https://auth.hutopy.com",
|
||||||
|
"Key": "b2df428b9929d3ace7c598bbf4e496b2f0b71ab3cd4f94540356cfc35b000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
|
||||||
"DefaultConnection": "Server={DB_HOST},1433;Database=Hutopy;User Id=sa;Password={DB_PASSWORD};MultipleActiveResultSets=true;TrustServerCertificate=True"
|
|
||||||
},
|
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
{
|
|
||||||
"runtime": "Net80",
|
|
||||||
"defaultVariables": null,
|
|
||||||
"documentGenerator": {
|
|
||||||
"aspNetCoreToOpenApi": {
|
|
||||||
"project": "Web.csproj",
|
|
||||||
"msBuildProjectExtensionsPath": null,
|
|
||||||
"configuration": null,
|
|
||||||
"runtime": null,
|
|
||||||
"targetFramework": null,
|
|
||||||
"noBuild": true,
|
|
||||||
"msBuildOutputPath": null,
|
|
||||||
"verbose": false,
|
|
||||||
"workingDirectory": null,
|
|
||||||
"requireParametersWithoutDefault": true,
|
|
||||||
"apiGroupNames": null,
|
|
||||||
"defaultPropertyNameHandling": "CamelCase",
|
|
||||||
"defaultReferenceTypeNullHandling": "Null",
|
|
||||||
"defaultDictionaryValueReferenceTypeNullHandling": "NotNull",
|
|
||||||
"defaultResponseReferenceTypeNullHandling": "NotNull",
|
|
||||||
"generateOriginalParameterNames": true,
|
|
||||||
"defaultEnumHandling": "Integer",
|
|
||||||
"flattenInheritanceHierarchy": false,
|
|
||||||
"generateKnownTypes": true,
|
|
||||||
"generateEnumMappingDescription": false,
|
|
||||||
"generateXmlObjects": false,
|
|
||||||
"generateAbstractProperties": false,
|
|
||||||
"generateAbstractSchemas": true,
|
|
||||||
"ignoreObsoleteProperties": false,
|
|
||||||
"allowReferencesWithProperties": false,
|
|
||||||
"useXmlDocumentation": true,
|
|
||||||
"resolveExternalXmlDocumentation": true,
|
|
||||||
"excludedTypeNames": [],
|
|
||||||
"serviceHost": null,
|
|
||||||
"serviceBasePath": null,
|
|
||||||
"serviceSchemes": [],
|
|
||||||
"infoTitle": "Hutopy API",
|
|
||||||
"infoDescription": null,
|
|
||||||
"infoVersion": "1.0.0",
|
|
||||||
"documentTemplate": null,
|
|
||||||
"documentProcessorTypes": [],
|
|
||||||
"operationProcessorTypes": [],
|
|
||||||
"typeNameGeneratorType": null,
|
|
||||||
"schemaNameGeneratorType": null,
|
|
||||||
"contractResolverType": null,
|
|
||||||
"serializerSettingsType": null,
|
|
||||||
"useDocumentProvider": true,
|
|
||||||
"documentName": "v1",
|
|
||||||
"aspNetCoreEnvironment": null,
|
|
||||||
"createWebHostBuilderMethod": null,
|
|
||||||
"startupType": null,
|
|
||||||
"allowNullableBodyParameters": true,
|
|
||||||
"useHttpAttributeNameAsOperationId": false,
|
|
||||||
"output": "wwwroot/api/specification.json",
|
|
||||||
"outputType": "OpenApi3",
|
|
||||||
"newLineBehavior": "Auto",
|
|
||||||
"assemblyPaths": [],
|
|
||||||
"assemblyConfig": null,
|
|
||||||
"referencePaths": [],
|
|
||||||
"useNuGetCache": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
5
src/Web/http-client.env.json
Normal file
5
src/Web/http-client.env.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dev": {
|
||||||
|
"base_url": "https://localhost:5001"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,869 +0,0 @@
|
|||||||
{
|
|
||||||
"x-generator": "NSwag v14.0.3.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))",
|
|
||||||
"openapi": "3.0.0",
|
|
||||||
"info": {
|
|
||||||
"title": "Hutopy API",
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"paths": {
|
|
||||||
"/api/GetMyUser": {
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"GetMyUser"
|
|
||||||
],
|
|
||||||
"operationId": "GetCurrentUser",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/UserDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"JWT": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/GetMyUser/profile-picture": {
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"GetMyUser"
|
|
||||||
],
|
|
||||||
"operationId": "GetCurrentUserProfilePicture",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/Stream"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"JWT": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/GetMyUser/profile-picture-2": {
|
|
||||||
"patch": {
|
|
||||||
"tags": [
|
|
||||||
"GetMyUser"
|
|
||||||
],
|
|
||||||
"operationId": "PatchApiGetMyUserProfilePicture2",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/Stream"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"JWT": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/JoinUs": {
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"JoinUs"
|
|
||||||
],
|
|
||||||
"operationId": "GetFutureCreators",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "PageNumber",
|
|
||||||
"in": "query",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "PageSize",
|
|
||||||
"in": "query",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"x-position": 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/PaginatedListOfFutureCreatorListDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"JoinUs"
|
|
||||||
],
|
|
||||||
"operationId": "CreateFutureCreator",
|
|
||||||
"requestBody": {
|
|
||||||
"x-name": "command",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/CreateFutureCreatorCommand"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "guid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/Stripe/confirmTransaction": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Stripe"
|
|
||||||
],
|
|
||||||
"operationId": "ConfirmTransaction",
|
|
||||||
"requestBody": {
|
|
||||||
"x-name": "command",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ConfirmStripeTransactionCommand"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/Stripe/getMyLastReceipt": {
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"Stripe"
|
|
||||||
],
|
|
||||||
"operationId": "GetMyLastReceipt",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "Email",
|
|
||||||
"in": "query",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "CreatorId",
|
|
||||||
"in": "query",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"x-position": 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/MyLastReceiptDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/Stripe": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Stripe"
|
|
||||||
],
|
|
||||||
"operationId": "CreateSessionCheckout",
|
|
||||||
"requestBody": {
|
|
||||||
"x-name": "command",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/CreateSessionCheckoutCommand"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/Users": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"operationId": "CreateUser",
|
|
||||||
"requestBody": {
|
|
||||||
"x-name": "command",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/CreateUserCommand"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "guid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"operationId": "GetMinimalUser",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "UserId",
|
|
||||||
"in": "query",
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "UserName",
|
|
||||||
"in": "query",
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"x-position": 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/MinimalUserDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/Users/login": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"operationId": "Login",
|
|
||||||
"requestBody": {
|
|
||||||
"x-name": "command",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/LoginCommand"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/Users/upload-profile-picture": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"operationId": "UploadProfilePicture",
|
|
||||||
"requestBody": {
|
|
||||||
"x-name": "stream",
|
|
||||||
"content": {
|
|
||||||
"application/octet-stream": {
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "binary",
|
|
||||||
"nullable": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/WeatherForecasts": {
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"WeatherForecasts"
|
|
||||||
],
|
|
||||||
"operationId": "GetWeatherForecasts",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/WeatherForecast"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"JWT": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/facebook/sign-in": {
|
|
||||||
"get": {
|
|
||||||
"tags": [
|
|
||||||
"Facebook"
|
|
||||||
],
|
|
||||||
"operationId": "Facebook_SignIn",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/google/sign-in": {
|
|
||||||
"post": {
|
|
||||||
"tags": [
|
|
||||||
"Google"
|
|
||||||
],
|
|
||||||
"operationId": "Google_SignIn",
|
|
||||||
"requestBody": {
|
|
||||||
"x-name": "request",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/GoogleSignInRequest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": true,
|
|
||||||
"x-position": 1
|
|
||||||
},
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "",
|
|
||||||
"content": {
|
|
||||||
"application/octet-stream": {
|
|
||||||
"schema": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "binary"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"components": {
|
|
||||||
"schemas": {
|
|
||||||
"UserDto": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "guid"
|
|
||||||
},
|
|
||||||
"firstName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"userName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"userTransactions": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/UserTransactionDto"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"userRoles": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"totalBalance": {
|
|
||||||
"type": "number",
|
|
||||||
"format": "decimal"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"UserTransactionDto": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"amount": {
|
|
||||||
"type": "number",
|
|
||||||
"format": "decimal"
|
|
||||||
},
|
|
||||||
"currency": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tipMessage": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"created": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "date-time"
|
|
||||||
},
|
|
||||||
"isConfirmed": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Stream": {
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/MarshalByRefObject"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"x-abstract": true,
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"canRead": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"canWrite": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"canSeek": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"canTimeout": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"length": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"position": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
},
|
|
||||||
"readTimeout": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"writeTimeout": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"MarshalByRefObject": {
|
|
||||||
"type": "object",
|
|
||||||
"x-abstract": true,
|
|
||||||
"additionalProperties": false
|
|
||||||
},
|
|
||||||
"PaginatedListOfFutureCreatorListDto": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"items": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/FutureCreatorListDto"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pageNumber": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"totalPages": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"totalCount": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"hasPreviousPage": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"hasNextPage": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"FutureCreatorListDto": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "guid"
|
|
||||||
},
|
|
||||||
"firstName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CreateFutureCreatorCommand": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"firstName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"emailAddress": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"phoneNumber": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"socialNetworkAccount": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"reasonToJoin": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ConfirmStripeTransactionCommand": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"object": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"created": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"$ref": "#/components/schemas/Data"
|
|
||||||
},
|
|
||||||
"request": {
|
|
||||||
"$ref": "#/components/schemas/Request"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Data": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"object": {
|
|
||||||
"$ref": "#/components/schemas/Object"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Object": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"amount": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"billing_details": {
|
|
||||||
"$ref": "#/components/schemas/BillingDetails"
|
|
||||||
},
|
|
||||||
"calculated_statement_descriptor": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"currency": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"paid": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"payment_intent": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"payment_method": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"receipt_url": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"failure_message": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"BillingDetails": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"email": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"phone": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Request": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MyLastReceiptDto": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"receiptUrl": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CreateSessionCheckoutCommand": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"creatorId": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"amount": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"currency": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tipMessage": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CreateUserCommand": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"firstName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"emailAddress": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"userName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LoginCommand": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"emailAddress": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"password": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MinimalUserDto": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"firstName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"userName": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"WeatherForecast": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"date": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "date-time"
|
|
||||||
},
|
|
||||||
"temperatureC": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"temperatureF": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"summary": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"GoogleSignInRequest": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {
|
|
||||||
"accessToken": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"securitySchemes": {
|
|
||||||
"JWT": {
|
|
||||||
"type": "apiKey",
|
|
||||||
"description": "Type into the textbox: Bearer {your JWT token}.",
|
|
||||||
"name": "Authorization",
|
|
||||||
"in": "header"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"JWT": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
9
start-infrastructure.sh
Normal file
9
start-infrastructure.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
docker run \
|
||||||
|
--cap-add SYS_PTRACE \
|
||||||
|
-e 'ACCEPT_EULA=1' \
|
||||||
|
-e 'MSSQL_SA_PASSWORD=P@ssword123!' \
|
||||||
|
-p 1433:1433 \
|
||||||
|
--name azuresqledge \
|
||||||
|
-d mcr.microsoft.com/azure-sql-edge
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
using Hutopy.Infrastructure.Data;
|
using Hutopy.Infrastructure.Data;
|
||||||
|
using Hutopy.Web;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Mvc.Testing;
|
using Microsoft.AspNetCore.Mvc.Testing;
|
||||||
using Microsoft.AspNetCore.TestHost;
|
using Microsoft.AspNetCore.TestHost;
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ public class TestcontainersTestDatabase : ITestDatabase
|
|||||||
|
|
||||||
var context = new ApplicationDbContext(options);
|
var context = new ApplicationDbContext(options);
|
||||||
|
|
||||||
context.Database.Migrate();
|
await context.Database.MigrateAsync();
|
||||||
|
|
||||||
_respawner = await Respawner.CreateAsync(_connectionString, new RespawnerOptions
|
_respawner = await Respawner.CreateAsync(_connectionString, new RespawnerOptions
|
||||||
{
|
{
|
||||||
TablesToIgnore = new Respawn.Graph.Table[] { "__EFMigrationsHistory" }
|
TablesToIgnore = ["__EFMigrationsHistory"]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user