using FastEndpoints; using Microsoft.EntityFrameworkCore; using Socialize.Api.Data; using Socialize.Api.Infrastructure.BlobStorage.Contracts; using Socialize.Api.Modules.Organizations.Data; using Socialize.Api.Modules.Organizations.Services; namespace Socialize.Api.Modules.Organizations.Handlers; public record ChangeOrganizationLogoRequest( IFormFile File); public record ChangeOrganizationLogoResponse( string BlobUrl); public sealed class ChangeOrganizationLogoRequestValidator : Validator { public ChangeOrganizationLogoRequestValidator() { RuleFor(x => x.File) .NotNull() .NotEmpty(); } } public class ChangeOrganizationLogoHandler( AppDbContext dbContext, IBlobStorage blobStorage, OrganizationAccessService organizationAccessService) : Endpoint { public override void Configure() { Post("/api/organizations/{organizationId:guid}/logo"); Options(o => o.WithTags("Organizations")); AllowFileUploads(); } public override async Task HandleAsync(ChangeOrganizationLogoRequest request, CancellationToken ct) { ArgumentNullException.ThrowIfNull(request); Guid organizationId = Route("organizationId"); Organization? organization = await dbContext.Organizations .SingleOrDefaultAsync(candidate => candidate.Id == organizationId, ct); if (organization is null) { await SendNotFoundAsync(ct); return; } if (!await organizationAccessService.HasOrganizationPermissionAsync( User, organizationId, OrganizationPermissions.ManageOrganizationSettings, ct)) { await SendForbiddenAsync(ct); return; } string blobUrl = await blobStorage.UploadFileAsync( ContainerNames.Organizations, $"{organization.Id}/{SubDirectoryNames.Profile}/{CommonFileNames.LogoPicture}", request.File.OpenReadStream(), request.File.ContentType, ct); organization.LogoUrl = blobUrl; await dbContext.SaveChangesAsync(ct); await SendOkAsync(new ChangeOrganizationLogoResponse(blobUrl), ct); } }