Fix query and API bugs: 403 for non-owners, page bounds, lifecycle filters, cover fallback, race-safe inserts, async album delete

This commit is contained in:
2026-09-24 05:14:05 +02:00
parent ab388e6ad6
commit 0e3576922f
90 changed files with 2083 additions and 322 deletions
@@ -0,0 +1,35 @@
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Musify.Application.Contracts;
using Musify.Infrastructure.MassTransit.Arguments;
namespace Musify.Infrastructure.MassTransit.Activities.Albums;
internal class DeleteAlbumFromDbActivity(
IDatabase database,
ILogger<DeleteAlbumFromDbActivity> logger)
: IExecuteActivity<DeleteAlbumFromDbArguments>
{
public const string ExecuteEndpointName = "delete-album-from-db";
public async Task<ExecutionResult> Execute(ExecuteContext<DeleteAlbumFromDbArguments> executeContext)
{
var album = await database.Albums
.SingleOrDefaultAsync(p => p.Id == executeContext.Arguments.AlbumId, executeContext.CancellationToken);
if (album == null)
{
logger.LogInformation(
"Album {AlbumId} not found while deleting from DB, skipping",
executeContext.Arguments.AlbumId);
return executeContext.Completed();
}
database.Albums.Remove(album);
await database.SaveChangesAsync(executeContext.CancellationToken);
logger.LogInformation("Deleted album {AlbumId} from DB", album.Id);
return executeContext.Completed();
}
}
@@ -0,0 +1,36 @@
using MassTransit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Musify.Application.Contracts;
using Musify.Domain.ValueObjects;
using Musify.Infrastructure.MassTransit.Arguments;
namespace Musify.Infrastructure.MassTransit.Activities.Albums;
internal class MarkAlbumAsRemovingActivity(
IDatabase database,
ILogger<MarkAlbumAsRemovingActivity> logger)
: IExecuteActivity<MarkAlbumAsRemovingArguments>
{
public const string ExecuteEndpointName = "mark-album-as-removing";
public async Task<ExecutionResult> Execute(ExecuteContext<MarkAlbumAsRemovingArguments> executeContext)
{
var album = await database.Albums
.SingleOrDefaultAsync(p => p.Id == executeContext.Arguments.AlbumId, executeContext.CancellationToken);
if (album == null)
{
logger.LogInformation(
"Album {AlbumId} not found while marking as removing, skipping",
executeContext.Arguments.AlbumId);
return executeContext.Completed();
}
album.LifeCycleStatus = LifeCycleStatus.Removing;
await database.SaveChangesAsync(executeContext.CancellationToken);
logger.LogInformation("Marked album {AlbumId} as removing", album.Id);
return executeContext.Completed();
}
}
@@ -23,7 +23,8 @@ internal class DownloadFileFromBucketActivity(
using var fileStream = await storageHandler.GetFileAsync(
executeContext.Arguments.Bucket,
executeContext.Arguments.Key,
executeContext.CancellationToken);
executeContext.CancellationToken)
?? throw new FileNotFoundException($"Object {executeContext.Arguments.Bucket}/{executeContext.Arguments.Key} was not found.");
var destinationDirectory = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrWhiteSpace(destinationDirectory))