Adds ChangeBanner for Creators
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
namespace Hutopy.Application.Common.Interfaces;
|
namespace Hutopy.Application.Common.Interfaces;
|
||||||
|
|
||||||
public interface IAzureBlobStorageService
|
public interface IBlobStorage
|
||||||
{
|
{
|
||||||
Task<string> UploadFileAsync(string containerName, string blobName, Stream stream, string contentType,
|
Task<string> UploadFileAsync(string containerName, string blobName, Stream stream, string contentType,
|
||||||
CancellationToken ct = default);
|
CancellationToken ct = default);
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
using Hutopy.Application.Utils;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace Hutopy.Application.Users.Commands;
|
namespace Hutopy.Application.Users.Commands;
|
||||||
@@ -16,19 +15,17 @@ public class UploadProfilePictureCommand : IRequest<IResult>
|
|||||||
public class UploadProfilePictureCommandHandler(
|
public class UploadProfilePictureCommandHandler(
|
||||||
IHttpContextAccessor contextAccessor,
|
IHttpContextAccessor contextAccessor,
|
||||||
IIdentityService identityService,
|
IIdentityService identityService,
|
||||||
IAzureBlobStorageService azureBlobStorageService) : IRequestHandler<UploadProfilePictureCommand, IResult>
|
IBlobStorage blobStorage) : IRequestHandler<UploadProfilePictureCommand, IResult>
|
||||||
{
|
{
|
||||||
public async Task<IResult> Handle(UploadProfilePictureCommand request, CancellationToken ct)
|
public async Task<IResult> Handle(UploadProfilePictureCommand request, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var contentType = contextAccessor.EnsureContentType();
|
|
||||||
|
|
||||||
var identityUser = await identityService.GetCurrentUserAsync();
|
var identityUser = await identityService.GetCurrentUserAsync();
|
||||||
|
|
||||||
var url = await azureBlobStorageService.UploadFileAsync(
|
var url = await blobStorage.UploadFileAsync(
|
||||||
ContainerNames.Users,
|
ContainerNames.Users,
|
||||||
$"{identityUser.Id}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}",
|
$"{identityUser.Id}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}",
|
||||||
request.File.OpenReadStream(),
|
request.File.OpenReadStream(),
|
||||||
contentType,
|
request.File.ContentType,
|
||||||
ct);
|
ct);
|
||||||
|
|
||||||
await identityService.UpdateCurrentUserPortraitUrlAsync(url);
|
await identityService.UpdateCurrentUserPortraitUrlAsync(url);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public record GetCurrentUserProfilePictureQuery : IRequest<Stream>;
|
|||||||
|
|
||||||
public class GetCurrentUserProfilePictureQueryHandler(
|
public class GetCurrentUserProfilePictureQueryHandler(
|
||||||
IIdentityService identityService,
|
IIdentityService identityService,
|
||||||
IAzureBlobStorageService azureBlobStorageService
|
IBlobStorage blobStorage
|
||||||
)
|
)
|
||||||
: IRequestHandler<GetCurrentUserProfilePictureQuery, Stream>
|
: IRequestHandler<GetCurrentUserProfilePictureQuery, Stream>
|
||||||
{
|
{
|
||||||
@@ -15,7 +15,7 @@ public class GetCurrentUserProfilePictureQueryHandler(
|
|||||||
{
|
{
|
||||||
var identityUser = await identityService.GetCurrentUserAsync();
|
var identityUser = await identityService.GetCurrentUserAsync();
|
||||||
|
|
||||||
return await azureBlobStorageService.DownloadFileAsync(
|
return await blobStorage.DownloadFileAsync(
|
||||||
ContainerNames.Users,
|
ContainerNames.Users,
|
||||||
$"{identityUser.Id.ToString()}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}",
|
$"{identityUser.Id.ToString()}/{SubDirectoryNames.Profile}/{CommonFileNames.ProfilePicture}",
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
|
|
||||||
namespace Hutopy.Application.Utils;
|
|
||||||
|
|
||||||
public static class HttpContextAccessorExtensions
|
|
||||||
{
|
|
||||||
public static HttpContext EnsureHttpContext(this IHttpContextAccessor httpContextAccessor)
|
|
||||||
{
|
|
||||||
if (httpContextAccessor.HttpContext == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("HttpContext is null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return httpContextAccessor.HttpContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string EnsureContentType(this IHttpContextAccessor httpContextAccessor)
|
|
||||||
{
|
|
||||||
var httpContext = EnsureHttpContext(httpContextAccessor);
|
|
||||||
var contentType = httpContext.Request.ContentType;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(contentType))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Content-Type header is missing.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return contentType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,13 +7,14 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Hutopy.Infrastructure.AzureBlob;
|
namespace Hutopy.Infrastructure.AzureBlob;
|
||||||
|
|
||||||
public class AzureBlobStorageService : IAzureBlobStorageService
|
public class AzureBlobStorage : IBlobStorage
|
||||||
{
|
{
|
||||||
private readonly BlobServiceClient _blobServiceClient;
|
private const long MaxUploadSize = 10 * 1024 * 1024; // 10 MB in bytes
|
||||||
private readonly ILogger<AzureBlobStorageService> _logger;
|
|
||||||
private readonly long _maxUploadSize = 10 * 1024 * 1024; // 10 MB in bytes
|
|
||||||
|
|
||||||
public AzureBlobStorageService(IConfiguration configuration, ILogger<AzureBlobStorageService> logger)
|
private readonly BlobServiceClient _blobServiceClient;
|
||||||
|
private readonly ILogger<AzureBlobStorage> _logger;
|
||||||
|
|
||||||
|
public AzureBlobStorage(IConfiguration configuration, ILogger<AzureBlobStorage> logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
var connectionString = configuration["Azure-Blob-Connection-String"] ?? "";
|
var connectionString = configuration["Azure-Blob-Connection-String"] ?? "";
|
||||||
@@ -37,12 +38,12 @@ public class AzureBlobStorageService : IAzureBlobStorageService
|
|||||||
stream.Position = 0;
|
stream.Position = 0;
|
||||||
|
|
||||||
// Check if the file size exceeds the maximum upload size
|
// Check if the file size exceeds the maximum upload size
|
||||||
if (stream.Length > _maxUploadSize)
|
if (stream.Length > MaxUploadSize)
|
||||||
{
|
{
|
||||||
_logger.LogError(
|
_logger.LogError(
|
||||||
$"Blob storage: File size exceeds the maximum allowed size of {_maxUploadSize} bytes.");
|
$"Blob storage: File size exceeds the maximum allowed size of {MaxUploadSize} bytes.");
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
$"Blob storage: File size exceeds the maximum allowed size of {_maxUploadSize} bytes.");
|
$"Blob storage: File size exceeds the maximum allowed size of {MaxUploadSize} bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate content type
|
// Validate content type
|
||||||
@@ -52,7 +52,7 @@ public static class DependencyInjection
|
|||||||
|
|
||||||
// Singleton services
|
// Singleton services
|
||||||
services.AddSingleton(TimeProvider.System);
|
services.AddSingleton(TimeProvider.System);
|
||||||
services.AddSingleton<IAzureBlobStorageService, AzureBlobStorageService>();
|
services.AddSingleton<IBlobStorage, AzureBlobStorage>();
|
||||||
|
|
||||||
// Scoped services
|
// Scoped services
|
||||||
services.AddScoped<IIdentityService, IdentityService>();
|
services.AddScoped<IIdentityService, IdentityService>();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using FastEndpoints;
|
using FastEndpoints;
|
||||||
using Hutopy.Application.AzureBlobStorage.Constants;
|
using Hutopy.Application.AzureBlobStorage.Constants;
|
||||||
using Hutopy.Application.Common.Interfaces;
|
using Hutopy.Application.Common.Interfaces;
|
||||||
using Hutopy.Application.Utils;
|
|
||||||
using Hutopy.Web.Features.Contents.Data;
|
using Hutopy.Web.Features.Contents.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ public record ChangeBannerRequest(
|
|||||||
public class ChangeBannerHandler(
|
public class ChangeBannerHandler(
|
||||||
IHttpContextAccessor contextAccessor,
|
IHttpContextAccessor contextAccessor,
|
||||||
ContentDbContext context,
|
ContentDbContext context,
|
||||||
IAzureBlobStorageService azureBlobStorageService)
|
IBlobStorage blobStorage)
|
||||||
: Endpoint<ChangeBannerRequest>
|
: Endpoint<ChangeBannerRequest>
|
||||||
{
|
{
|
||||||
public override void Configure()
|
public override void Configure()
|
||||||
@@ -29,19 +28,25 @@ public class ChangeBannerHandler(
|
|||||||
var creator = await context
|
var creator = await context
|
||||||
.Creators
|
.Creators
|
||||||
.Include(c => c.StoredDataUrls)
|
.Include(c => c.StoredDataUrls)
|
||||||
.SingleAsync(
|
.SingleOrDefaultAsync(
|
||||||
c => c.Id == request.CreatorId,
|
c => c.Id == request.CreatorId,
|
||||||
cancellationToken: ct);
|
cancellationToken: ct);
|
||||||
|
|
||||||
var contentType = contextAccessor.EnsureContentType();
|
if (creator is null)
|
||||||
|
{
|
||||||
|
await SendNotFoundAsync(ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var blobUrl = await azureBlobStorageService.UploadFileAsync(
|
var blobUrl = await blobStorage.UploadFileAsync(
|
||||||
ContainerNames.Users,
|
ContainerNames.Users,
|
||||||
$"{request.CreatorId}/{SubDirectoryNames.Profile}/{CommonFileNames.BannerPicture}",
|
$"{request.CreatorId}/{SubDirectoryNames.Profile}/{CommonFileNames.BannerPicture}",
|
||||||
request.File.OpenReadStream(),
|
request.File.OpenReadStream(),
|
||||||
contentType,
|
request.File.ContentType,
|
||||||
ct);
|
ct);
|
||||||
|
|
||||||
|
creator.StoredDataUrls.BannerPictureUrl = blobUrl;
|
||||||
|
|
||||||
await context.SaveChangesAsync(ct);
|
await context.SaveChangesAsync(ct);
|
||||||
|
|
||||||
await SendOkAsync(blobUrl, ct);
|
await SendOkAsync(blobUrl, ct);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public sealed class PostContentRequestValidator : Validator<PostContentRequest>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public sealed class PostContent(
|
public sealed class PostContent(
|
||||||
IAzureBlobStorageService blobStorage,
|
IBlobStorage blobStorage,
|
||||||
ContentDbContext context)
|
ContentDbContext context)
|
||||||
: Endpoint<PostContentRequest>
|
: Endpoint<PostContentRequest>
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user