Split creators out of identity

This commit is contained in:
Jonathan Bourdon
2024-07-31 23:29:26 -04:00
parent bbcc7a8a33
commit 2b30e1a03c
105 changed files with 1497 additions and 7490 deletions

View File

@@ -0,0 +1,49 @@
using FastEndpoints;
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Utils;
using Hutopy.Web.Features.Contents.Data;
using Microsoft.EntityFrameworkCore;
namespace Hutopy.Web.Features.Contents.Handlers;
public record ChangeBannerRequest(
Guid CreatorId,
IFormFile File);
public class ChangeBannerHandler(
IHttpContextAccessor contextAccessor,
ContentDbContext context,
IAzureBlobStorageService azureBlobStorageService)
: Endpoint<ChangeBannerRequest>
{
public override void Configure()
{
Post("/api/creators/{CreatorId}/banner");
Options(o => o.WithTags("Contents"));
AllowFileUploads();
}
public override async Task HandleAsync(ChangeBannerRequest request, CancellationToken ct)
{
var creator = await context
.Creators
.Include(c => c.StoredDataUrls)
.SingleAsync(
c => c.Id == request.CreatorId,
cancellationToken: ct);
var contentType = contextAccessor.EnsureContentType();
var blobUrl = await azureBlobStorageService.UploadFileAsync(
ContainerNames.Users,
$"{request.CreatorId}/{SubDirectoryNames.Profile}/{CommonFileNames.BannerPicture}",
request.File.OpenReadStream(),
contentType,
ct);
await context.SaveChangesAsync(ct);
await SendOkAsync(blobUrl, ct);
}
}

View File

@@ -0,0 +1,71 @@
using FastEndpoints;
using FluentValidation;
using Hutopy.Web.Features.Contents.Data;
using Microsoft.EntityFrameworkCore;
namespace Hutopy.Web.Features.Contents.Handlers;
public record ChangeColorsRequest(
Guid CreatorId,
string? BannerTop,
string? BannerBottom,
string? Accent,
string? Menu);
public sealed class ChangeColorsRequestValidator
: Validator<ChangeColorsRequest>
{
public ChangeColorsRequestValidator()
{
RuleFor(x => x.BannerTop)
.MinimumLength(4).WithMessage("The minimum value should be in the format #444")
.MinimumLength(9).WithMessage("The maximum value should be in the format #11223344")
.Must(x => x.StartsWith('#')).WithMessage("The format should be a valid html color and start with #");
RuleFor(x => x.BannerBottom)
.MinimumLength(4).WithMessage("The minimum value should be in the format #444")
.MinimumLength(9).WithMessage("The maximum value should be in the format #11223344")
.Must(x => x.StartsWith('#')).WithMessage("The format should be a valid html color and start with #");
RuleFor(x => x.Accent)
.MinimumLength(4).WithMessage("The minimum value should be in the format #444")
.MinimumLength(9).WithMessage("The maximum value should be in the format #11223344")
.Must(x => x.StartsWith('#')).WithMessage("The format should be a valid html color and start with #");
RuleFor(x => x.Menu)
.MinimumLength(4).WithMessage("The minimum value should be in the format #444")
.MinimumLength(9).WithMessage("The maximum value should be in the format #11223344")
.Must(x => x.StartsWith('#')).WithMessage("The format should be a valid html color and start with #");
}
}
public class ChangeColorsHandler(
ContentDbContext context)
: Endpoint<ChangeColorsRequest>
{
public override void Configure()
{
Post("/api/creators/{CreatorId}/colors");
Options(o => o.WithTags("Contents"));
AllowFileUploads();
}
public override async Task HandleAsync(ChangeColorsRequest request, CancellationToken ct)
{
var creator = await context
.Creators
.Include(c => c.ProfileColors)
.SingleAsync(
c => c.Id == request.CreatorId,
cancellationToken: ct);
creator.ProfileColors.BannerTop = request.BannerTop;
creator.ProfileColors.BannerBottom = request.BannerBottom;
creator.ProfileColors.Accent = request.Accent;
creator.ProfileColors.Menu = request.Menu;
await context.SaveChangesAsync(ct);
await SendOkAsync(ct);
}
}

View File

@@ -53,6 +53,7 @@ public sealed class PostContent(
PostContentRequest req,
CancellationToken ct)
{
var urls = new ConcurrentBag<string>();
await Parallel.ForEachAsync(
@@ -98,14 +99,11 @@ public sealed class PostContent(
IFormFile file,
CancellationToken ct = default)
{
var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream, ct);
// TODO: I would like us to use ContainerNames.Creators but it seems we are missing configurations @jbourdon
var url = await blobStorage.UploadFileAsync(
ContainerNames.Users,
$"{creatorId}/{SubDirectoryNames.Contents}/{contentId}/{file.FileName}",
memoryStream,
file.OpenReadStream(),
file.ContentType,
ct: ct);

View File

@@ -0,0 +1,53 @@
using FastEndpoints;
using FluentValidation;
using Hutopy.Web.Common;
using Hutopy.Web.Features.Contents.Data;
namespace Hutopy.Web.Features.Contents.Handlers;
public record CreateCreatorRequest(
Guid CreatorId,
string Name);
public sealed class CreateCreatorRequestValidator : Validator<CreateCreatorRequest>
{
public CreateCreatorRequestValidator()
{
RuleFor(r => r.CreatorId)
.NotNull().WithMessage("You should specify the CreatorId")
.NotEmpty().WithMessage("You should specify a valid/not empty CreatorId");
RuleFor(r => r.Name)
.NotNull().WithMessage("You should specify the Name")
.NotEmpty().WithMessage("You should specify a valid/not empty Name");
}
}
public sealed class CreateCreatorHandler(
ContentDbContext context)
: Endpoint<CreateCreatorRequest>
{
public override void Configure()
{
Post("/api/creators");
Options(o => o.WithTags("Contents"));
}
public override async Task HandleAsync(
CreateCreatorRequest req,
CancellationToken ct)
{
await context.Creators.AddAsync(
new()
{
Id = req.CreatorId,
CreatedBy = User.GetUserId(),
Name = req.Name
},
ct);
await context.SaveChangesAsync(ct);
await SendOkAsync(ct);
}
}

View File

@@ -1,13 +1,13 @@
using FastEndpoints;
using FluentValidation;
using Hutopy.Application.Common.Interfaces;
using Hutopy.Application.Common.Models;
using Hutopy.Web.Features.Contents.Data;
using Microsoft.EntityFrameworkCore;
namespace Hutopy.Web.Features.Contents.Handlers;
public sealed class GetCreatorByAliasRequest
{
public string CreatorAlias { get; init; }
public required string Name { get; set; }
}
public sealed class GetCreatorByAliasRequestValidator
@@ -15,20 +15,20 @@ public sealed class GetCreatorByAliasRequestValidator
{
public GetCreatorByAliasRequestValidator()
{
RuleFor(r => r.CreatorAlias)
.NotNull().WithMessage("You should specify the CreatorAlias")
.NotEmpty().WithMessage("You should specify a valid/not empty CreatorAlias");
RuleFor(r => r.Name)
.NotNull().WithMessage("You should specify the Name")
.NotEmpty().WithMessage("You should specify a valid/not empty Name");
}
}
public class GetCreatorByAlias(
IIdentityService identityService)
: Endpoint<GetCreatorByAliasRequest, UserModel?>
public class GetCreatorByAliasHandler(
ContentDbContext context)
: Endpoint<GetCreatorByAliasRequest, Creator>
{
public override void Configure()
{
Get("/api/creators/@{Name}");
Options((o => o.WithTags("Creators")));
Get("/api/creators/@{CreatorAlias}");
AllowAnonymous();
}
@@ -36,10 +36,13 @@ public class GetCreatorByAlias(
GetCreatorByAliasRequest req,
CancellationToken ct)
{
var user = await identityService.FindUserByCreatorAliasAsync(
req.CreatorAlias,
ct);
var creator = await context
.Creators
.SingleOrDefaultAsync(
c => EF.Functions.Like(c.Name, req.Name),
cancellationToken: ct);
await SendAsync(user, cancellation: ct);
if (creator is null) await SendNotFoundAsync(ct);
else await SendAsync(creator, cancellation: ct);
}
}

View File

@@ -0,0 +1,47 @@
using FastEndpoints;
using FluentValidation;
using Hutopy.Web.Features.Contents.Data;
namespace Hutopy.Web.Features.Contents.Handlers;
public sealed class GetCreatorByIdRequest
{
public required Guid CreatorId { get; set; }
}
public sealed class GetCreatorByIdRequestValidator
: Validator<GetCreatorByIdRequest>
{
public GetCreatorByIdRequestValidator()
{
RuleFor(r => r.CreatorId)
.NotNull().WithMessage("You should specify the CreatorId")
.NotEmpty().WithMessage("You should specify a valid/not empty CreatorId");
}
}
public class GetCreatorByIdHandler(
ContentDbContext context)
: Endpoint<GetCreatorByIdRequest, Creator>
{
public override void Configure()
{
Get("/api/creators/{CreatorId}");
Options((o => o.WithTags("Creators")));
AllowAnonymous();
}
public override async Task HandleAsync(
GetCreatorByIdRequest req,
CancellationToken ct)
{
var creator = await context
.Creators
.FindAsync(
[req.CreatorId],
cancellationToken: ct);
if (creator is null) await SendNotFoundAsync(ct);
else await SendAsync(creator, cancellation: ct);
}
}