This commit is contained in:
2026-03-20 20:24:10 +01:00
parent c378c7a0dd
commit 61f4435cc2
7 changed files with 134 additions and 115 deletions
@@ -11,8 +11,7 @@ namespace Musify.Infrastructure.Configuration
serviceDescriptors serviceDescriptors
.AddValidatedConfiguration<DatabaseConfiguration>(configuration, DatabaseConfiguration.SectionName) .AddValidatedConfiguration<DatabaseConfiguration>(configuration, DatabaseConfiguration.SectionName)
.AddValidatedConfiguration<KeycloakConfiguration>(configuration, KeycloakConfiguration.SectionName) .AddValidatedConfiguration<KeycloakConfiguration>(configuration, KeycloakConfiguration.SectionName)
.AddValidatedConfiguration<StorageClientConfiguration>(configuration, StorageClientConfiguration.SectionName) .AddValidatedConfiguration<StorageClientConfiguration>(configuration, StorageClientConfiguration.SectionName);
.AddValidatedConfiguration<WorkerStorageConfiguration>(configuration, WorkerStorageConfiguration.SectionName);
return serviceDescriptors; return serviceDescriptors;
} }
@@ -1,21 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Musify.Infrastructure.Configuration
{
public class WorkerStorageConfiguration
{
public const string SectionName = "WorkerStorage";
public Routes Routes { get; set; } = new Routes();
[Required]
[MinLength(3)]
public required string BucketName { get; set; }
}
public class Routes
{
[Required]
public string TemporaryFiles { get; set; } = "TemporaryFiles";
}
}
@@ -6,24 +6,27 @@ using Musify.Infrastructure.Messaging.Activities.Arguments;
namespace Musify.Infrastructure.Messaging.Activities namespace Musify.Infrastructure.Messaging.Activities
{ {
public class RemoveFilesActivity(IStorageHandler storageHandler, ILogger<RemoveFilesActivity> logger) public class RemoveFilesActivity(IStorageHandler storageHandler, ILogger<RemoveFilesActivity> logger)
: IExecuteActivity<RemoveFileArguments[]> : IExecuteActivity<RemoveFileArguments>
{ {
public async Task<ExecutionResult> Execute(ExecuteContext<RemoveFileArguments[]> executeContext) public const string ExecuteEndpointName = "Remove-File";
{
foreach (var file in executeContext.Arguments) public async Task<ExecutionResult> Execute(ExecuteContext<RemoveFileArguments> executeContext)
{ {
var removeFile = await storageHandler.RemoveFileAsync( var removeFile = await storageHandler.RemoveFileAsync(
file.BucketName, executeContext.Arguments.BucketName,
file.KeyName, executeContext.Arguments.KeyName,
executeContext.CancellationToken); executeContext.CancellationToken);
if (!removeFile.IsSuccess) if (!removeFile.IsSuccess)
{ {
string errorMessage = string.Join(";", removeFile.Errors); string errorMessage = string.Join(";", removeFile.Errors);
logger.LogWarning("Failed to remove file {File} from bucket {BucketName}: {ErrorMessage}", logger.LogWarning(
file, file.BucketName, errorMessage); "Failed to remove file {KeyName} from bucket {BucketName}: {ErrorMessage}",
} executeContext.Arguments.KeyName,
executeContext.Arguments.BucketName,
errorMessage);
} }
return executeContext.Completed(); return executeContext.Completed();
} }
} }
@@ -7,6 +7,8 @@ namespace Musify.Infrastructure.Messaging.Activities
public class ResizePictureActivity(IStorageHandler storageHandler, IPictureHandler pictureHandler) public class ResizePictureActivity(IStorageHandler storageHandler, IPictureHandler pictureHandler)
: IExecuteActivity<ResizePictureArgument> : IExecuteActivity<ResizePictureArgument>
{ {
public const string ExecuteEndpointName = "Resize-Picture";
public async Task<ExecutionResult> Execute(ExecuteContext<ResizePictureArgument> executeContext) public async Task<ExecutionResult> Execute(ExecuteContext<ResizePictureArgument> executeContext)
{ {
var file = await storageHandler.GetFileAsync( var file = await storageHandler.GetFileAsync(
@@ -1,4 +1,5 @@
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore;
using Musify.Application.Contracts.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.Messaging.Activities.Arguments; using Musify.Infrastructure.Messaging.Activities.Arguments;
@@ -7,20 +8,21 @@ namespace Musify.Infrastructure.Messaging.Activities
public class UpdatePlayListPictureActivity(IDatabase database) public class UpdatePlayListPictureActivity(IDatabase database)
: IExecuteActivity<UpdatePlayListPictureArguments> : IExecuteActivity<UpdatePlayListPictureArguments>
{ {
public const string ExecuteEndpointName = "Update-PlayList-Picture";
public async Task<ExecutionResult> Execute(ExecuteContext<UpdatePlayListPictureArguments> executeContext) public async Task<ExecutionResult> Execute(ExecuteContext<UpdatePlayListPictureArguments> executeContext)
{ {
var PlayList = await database.PlayLists.FindAsync(executeContext.Arguments.PlayListId); var playList = await database.PlayLists.SingleOrDefaultAsync(p => p.Id == executeContext.Arguments.PlayListId,
executeContext.CancellationToken);
if (PlayList is null) if (playList is null)
{
throw new Exception($"PlayList with id {executeContext.Arguments.PlayListId} not found."); throw new Exception($"PlayList with id {executeContext.Arguments.PlayListId} not found.");
}
PlayList.SmallPictureKeyName = executeContext.Arguments.SmallPictureKeyName; playList.SmallPictureKeyName = executeContext.Arguments.SmallPictureKeyName;
PlayList.MediumPictureKeyName = executeContext.Arguments.MediumPictureKeyName; playList.MediumPictureKeyName = executeContext.Arguments.MediumPictureKeyName;
PlayList.LargePictureKeyName = executeContext.Arguments.LargePictureKeyName; playList.LargePictureKeyName = executeContext.Arguments.LargePictureKeyName;
database.PlayLists.Update(PlayList); database.PlayLists.Update(playList);
await database.SaveChangesAsync(executeContext.CancellationToken); await database.SaveChangesAsync(executeContext.CancellationToken);
return executeContext.Completed(); return executeContext.Completed();
@@ -2,6 +2,7 @@
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Application.Contracts.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Events; using Musify.Application.Events;
using Musify.Infrastructure.Messaging.Activities;
using Musify.Infrastructure.Messaging.Activities.Arguments; using Musify.Infrastructure.Messaging.Activities.Arguments;
namespace Musify.Infrastructure.Messaging.Consumers namespace Musify.Infrastructure.Messaging.Consumers
@@ -26,49 +27,38 @@ namespace Musify.Infrastructure.Messaging.Consumers
playListConfiguration.Routes.LargePictures, playListConfiguration.Routes.LargePictures,
$"{consumeContext.Message.PlayListId}_large.{pictureHandler.FileExtension}"); $"{consumeContext.Message.PlayListId}_large.{pictureHandler.FileExtension}");
routingSlipBuilder.AddActivity( AddResizeActivity(
routingSlipBuilder,
"ResizeSmall", "ResizeSmall",
new Uri("Queue:Resize-Picture_Execute"), smallPictureKeyName,
new ResizePictureArgument(
storageSettings.BucketName,
consumeContext.Message.OriginalPictureKeyName,
storageSettings.BucketName,
playListConfiguration.Routes.SmallPictures,
playListConfiguration.PicturesSizes.SmallPictureWidth, playListConfiguration.PicturesSizes.SmallPictureWidth,
playListConfiguration.PicturesSizes.SmallPictureHeight)); playListConfiguration.PicturesSizes.SmallPictureHeight,
consumeContext.Message.OriginalPictureKeyName);
routingSlipBuilder.AddActivity( AddResizeActivity(
routingSlipBuilder,
"ResizeMedium", "ResizeMedium",
new Uri("Queue:Resize-Picture_Execute"), mediumPictureKeyName,
new ResizePictureArgument(
storageSettings.BucketName,
consumeContext.Message.OriginalPictureKeyName,
storageSettings.BucketName,
playListConfiguration.Routes.MediumPictures,
playListConfiguration.PicturesSizes.MediumPictureWidth, playListConfiguration.PicturesSizes.MediumPictureWidth,
playListConfiguration.PicturesSizes.MediumPictureHeight)); playListConfiguration.PicturesSizes.MediumPictureHeight,
consumeContext.Message.OriginalPictureKeyName);
routingSlipBuilder.AddActivity( AddResizeActivity(
routingSlipBuilder,
"ResizeLarge", "ResizeLarge",
new Uri("Queue:Resize-Picture_Execute"), largePictureKeyName,
new ResizePictureArgument(
storageSettings.BucketName,
consumeContext.Message.OriginalPictureKeyName,
storageSettings.BucketName,
playListConfiguration.Routes.LargePictures,
playListConfiguration.PicturesSizes.LargePictureWidth, playListConfiguration.PicturesSizes.LargePictureWidth,
playListConfiguration.PicturesSizes.LargePictureHeight)); playListConfiguration.PicturesSizes.LargePictureHeight,
consumeContext.Message.OriginalPictureKeyName);
routingSlipBuilder.AddActivity( routingSlipBuilder.AddActivity(
"DeleteOriginal", "DeleteOriginal",
new Uri("Queue:Delete-Picture_Execute"), BuildExecuteUri(RemoveFilesActivity.ExecuteEndpointName),
new RemoveFileArguments( new RemoveFileArguments(storageSettings.BucketName, consumeContext.Message.OriginalPictureKeyName));
storageSettings.BucketName,
consumeContext.Message.OriginalPictureKeyName));
routingSlipBuilder.AddActivity( routingSlipBuilder.AddActivity(
"UpdatePlayListPicture", "UpdatePlayListPicture",
new Uri("Queue:Update-PlayList-Picture_Execute"), BuildExecuteUri(UpdatePlayListPictureActivity.ExecuteEndpointName),
new UpdatePlayListPictureArguments( new UpdatePlayListPictureArguments(
consumeContext.Message.PlayListId, consumeContext.Message.PlayListId,
smallPictureKeyName, smallPictureKeyName,
@@ -78,5 +68,27 @@ namespace Musify.Infrastructure.Messaging.Consumers
var routingSlip = routingSlipBuilder.Build(); var routingSlip = routingSlipBuilder.Build();
await bus.Execute(routingSlip); await bus.Execute(routingSlip);
} }
void AddResizeActivity(
RoutingSlipBuilder routingSlipBuilder,
string activityName,
string destinationKeyName,
int width,
int height,
string originalPictureKeyName)
{
routingSlipBuilder.AddActivity(
activityName,
BuildExecuteUri(ResizePictureActivity.ExecuteEndpointName),
new ResizePictureArgument(
storageSettings.BucketName,
originalPictureKeyName,
storageSettings.BucketName,
destinationKeyName,
width,
height));
}
static Uri BuildExecuteUri(string endpointName) => new($"queue:{endpointName}_execute");
} }
} }
@@ -1,6 +1,8 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Musify.Infrastructure.Messaging.Activities;
using Musify.Infrastructure.Messaging.Activities.Arguments;
using Musify.Infrastructure.Messaging.Consumers; using Musify.Infrastructure.Messaging.Consumers;
namespace Musify.Infrastructure.Messaging namespace Musify.Infrastructure.Messaging
@@ -11,26 +13,10 @@ namespace Musify.Infrastructure.Messaging
{ {
serviceDescriptors.AddMassTransit(options => serviceDescriptors.AddMassTransit(options =>
{ {
options.UsingRabbitMq((busRegistrationContext, busFactoryConfigurator) => options.UsingRabbitMq((_, busFactoryConfigurator) =>
{ ConfigureRabbitMqHost(busFactoryConfigurator, configuration));
var section = configuration.GetSection("MassTransit");
var host = section["Host"]
?? throw new InvalidOperationException("MassTransit:Host configuration value not found.");
var username = section["Username"]
?? throw new InvalidOperationException("MassTransit:Username configuration value not found.");
var password = section["Password"]
?? throw new InvalidOperationException("MassTransit:Password configuration value not found.");
busFactoryConfigurator.Host(host, options =>
{
options.Username(username);
options.Password(password);
} );
} );
} ); } );
return serviceDescriptors; return serviceDescriptors;
} }
@@ -39,8 +25,40 @@ namespace Musify.Infrastructure.Messaging
serviceDescriptors.AddMassTransit(options => serviceDescriptors.AddMassTransit(options =>
{ {
options.AddConsumer<UpdatePlayListPictureConsumer>(); options.AddConsumer<UpdatePlayListPictureConsumer>();
options.AddExecuteActivity<ResizePictureActivity, ResizePictureArgument>();
options.AddExecuteActivity<RemoveFilesActivity, RemoveFileArguments>();
options.AddExecuteActivity<UpdatePlayListPictureActivity, UpdatePlayListPictureArguments>();
options.UsingRabbitMq((busRegistrationContext, busFactoryConfigurator) => options.UsingRabbitMq((busRegistrationContext, busFactoryConfigurator) =>
{
ConfigureRabbitMqHost(busFactoryConfigurator, configuration);
busFactoryConfigurator.ReceiveEndpoint(UpdatePlayListPictureConsumer.QueueName, endpointConfigurator =>
{
endpointConfigurator.ConfigureConsumer<UpdatePlayListPictureConsumer>(busRegistrationContext);
} );
ConfigureExecuteActivityEndpoint<ResizePictureActivity, ResizePictureArgument>(
busFactoryConfigurator,
busRegistrationContext,
ResizePictureActivity.ExecuteEndpointName);
ConfigureExecuteActivityEndpoint<RemoveFilesActivity, RemoveFileArguments>(
busFactoryConfigurator,
busRegistrationContext,
RemoveFilesActivity.ExecuteEndpointName);
ConfigureExecuteActivityEndpoint<UpdatePlayListPictureActivity, UpdatePlayListPictureArguments>(
busFactoryConfigurator,
busRegistrationContext,
UpdatePlayListPictureActivity.ExecuteEndpointName);
} );
} );
return serviceDescriptors;
}
static void ConfigureRabbitMqHost(IRabbitMqBusFactoryConfigurator busFactoryConfigurator, IConfiguration configuration)
{ {
var section = configuration.GetSection("MassTransit"); var section = configuration.GetSection("MassTransit");
@@ -58,15 +76,19 @@ namespace Musify.Infrastructure.Messaging
options.Username(username); options.Username(username);
options.Password(password); options.Password(password);
} ); } );
}
busFactoryConfigurator.ReceiveEndpoint(UpdatePlayListPictureConsumer.QueueName, endpointConfigurator => static void ConfigureExecuteActivityEndpoint<TActivity, TArguments>(
IRabbitMqBusFactoryConfigurator busFactoryConfigurator,
IBusRegistrationContext busRegistrationContext,
string endpointName)
where TActivity : class, IExecuteActivity<TArguments>
where TArguments : class
{ {
endpointConfigurator.ConfigureConsumer<UpdatePlayListPictureConsumer>(busRegistrationContext); busFactoryConfigurator.ReceiveEndpoint($"{endpointName}_execute", endpointConfigurator =>
{
endpointConfigurator.ExecuteActivityHost<TActivity, TArguments>(busRegistrationContext);
} ); } );
} );
} );
return serviceDescriptors;
} }
} }
} }