Fixes and a lot of changes

This commit is contained in:
2026-05-14 22:13:22 +02:00
parent 80f44422a0
commit f62cf057d2
62 changed files with 113 additions and 182 deletions
@@ -1,23 +0,0 @@
using Ardalis.Result;
using Microsoft.Extensions.Logging;
using Musify.Application.Configuration;
using Musify.Domain.Entities;
namespace Musify.Application.Abstractions.Application;
public interface IUploadIntentService
{
Task<Result> CheckQuotaAsync(
UploadIntentConfiguration config,
ILogger logger,
Guid userId,
long requiredBytes,
int requiredIntentCount,
CancellationToken cancellationToken);
Task<Result<UploadIntent>> ValidateAndLoadAsync(
UploadIntentConfiguration config,
Guid intentId,
Guid userId,
CancellationToken cancellationToken);
}
@@ -1,4 +1,4 @@
namespace Musify.Application.Abstractions.Application namespace Musify.Application.Contracts.Application
{ {
public interface IFileData public interface IFileData
{ {
@@ -1,4 +1,4 @@
namespace Musify.Application.Abstractions.Infrastructure namespace Musify.Application.Contracts.Infrastructure
{ {
public interface IAudioTranscoderService public interface IAudioTranscoderService
{ {
@@ -1,7 +1,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Application.Abstractions.Infrastructure namespace Musify.Application.Contracts.Infrastructure
{ {
public interface IDatabase public interface IDatabase
{ {
@@ -1,4 +1,4 @@
namespace Musify.Application.Abstractions.Infrastructure namespace Musify.Application.Contracts.Infrastructure
{ {
public interface IDatabaseTransaction : IAsyncDisposable public interface IDatabaseTransaction : IAsyncDisposable
{ {
@@ -1,4 +1,4 @@
namespace Musify.Application.Abstractions.Infrastructure namespace Musify.Application.Contracts.Infrastructure
{ {
public interface IEventBus public interface IEventBus
{ {
@@ -1,4 +1,4 @@
namespace Musify.Application.Abstractions.Infrastructure namespace Musify.Application.Contracts.Infrastructure
{ {
public interface IPictureService public interface IPictureService
{ {
@@ -1,4 +1,4 @@
namespace Musify.Application.Abstractions.Infrastructure namespace Musify.Application.Contracts.Infrastructure
{ {
public interface IStorageService public interface IStorageService
{ {
@@ -1,4 +1,4 @@
namespace Musify.Application.Abstractions.Infrastructure namespace Musify.Application.Contracts.Infrastructure
{ {
public record ObjectMetadata(string ContentType, long ContentLength); public record ObjectMetadata(string ContentType, long ContentLength);
} }
@@ -1,6 +1,6 @@
using X.PagedList; using X.PagedList;
namespace Musify.Application.Abstractions.Application namespace Musify.Application.Pagination
{ {
public record PaginatedResponse<T>( public record PaginatedResponse<T>(
IReadOnlyCollection<T> Items, IReadOnlyCollection<T> Items,
@@ -3,13 +3,13 @@ using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Events; using Musify.Application.Events;
using Musify.Application.PlayLists.Commands; using Musify.Application.PlayLists.Commands;
using Musify.Application.PlayLists.Responses; using Musify.Application.PlayLists.Responses;
using Musify.Application.UploadIntents; using Musify.Application.UploadIntents;
using Musify.Application.Extensions; using Musify.Application.Extensions;
using Musify.Application.Abstractions.Application; using Musify.Application.Services;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Application.PlayLists.Handlers namespace Musify.Application.PlayLists.Handlers
@@ -17,7 +17,7 @@ namespace Musify.Application.PlayLists.Handlers
public class CreatePlayListCommandHandler( public class CreatePlayListCommandHandler(
IEventBus eventBus, IEventBus eventBus,
IDatabase database, IDatabase database,
IUploadIntentService uploadIntentService, UploadIntentValidator uploadIntentValidator,
IStorageService storageService, IStorageService storageService,
ILogger<CreatePlayListCommandHandler> logger, ILogger<CreatePlayListCommandHandler> logger,
ApplicationStorageConfiguration storageConfiguration, ApplicationStorageConfiguration storageConfiguration,
@@ -41,7 +41,7 @@ namespace Musify.Application.PlayLists.Handlers
if (request.PictureIntentId.HasValue) if (request.PictureIntentId.HasValue)
{ {
var validation = await uploadIntentService.ValidateAndLoadAsync( var validation = await uploadIntentValidator.ValidateAndLoadAsync(
uploadIntentConfiguration, uploadIntentConfiguration,
request.PictureIntentId.Value, request.UserId, cancellationToken); request.PictureIntentId.Value, request.UserId, cancellationToken);
if (!validation.IsSuccess) if (!validation.IsSuccess)
@@ -2,7 +2,7 @@ using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Events; using Musify.Application.Events;
using Musify.Application.PlayLists.Commands; using Musify.Application.PlayLists.Commands;
using Musify.Domain.Entities; using Musify.Domain.Entities;
@@ -1,6 +1,6 @@
using Ardalis.Result; using Ardalis.Result;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.PlayLists.Responses; using Musify.Application.PlayLists.Responses;
using Musify.Application.PlayLists.Queries; using Musify.Application.PlayLists.Queries;
using Mediator; using Mediator;
@@ -1,7 +1,7 @@
using Ardalis.Result; using Ardalis.Result;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.PlayLists.Responses; using Musify.Application.PlayLists.Responses;
using Musify.Application.PlayLists.Queries; using Musify.Application.PlayLists.Queries;
using X.PagedList.EF; using X.PagedList.EF;
@@ -1,7 +1,7 @@
using Ardalis.Result; using Ardalis.Result;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.PlayLists.Responses; using Musify.Application.PlayLists.Responses;
using Musify.Application.PlayLists.Queries; using Musify.Application.PlayLists.Queries;
using X.PagedList.EF; using X.PagedList.EF;
@@ -2,19 +2,18 @@ using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Abstractions.Application;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Application.PlayLists.Commands; using Musify.Application.PlayLists.Commands;
using Musify.Application.PlayLists.Responses; using Musify.Application.PlayLists.Responses;
using Musify.Application.UploadIntents; using Musify.Application.Services;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Application.PlayLists.Handlers namespace Musify.Application.PlayLists.Handlers
{ {
public class RequestPlayListPictureUploadCommandHandler( public class RequestPlayListPictureUploadCommandHandler(
IDatabase database, IDatabase database,
IUploadIntentService uploadIntentService, UploadIntentValidator uploadIntentValidator,
IStorageService storageService, IStorageService storageService,
ILogger<RequestPlayListPictureUploadCommandHandler> logger, ILogger<RequestPlayListPictureUploadCommandHandler> logger,
ApplicationStorageConfiguration storageConfiguration, ApplicationStorageConfiguration storageConfiguration,
@@ -22,8 +21,6 @@ namespace Musify.Application.PlayLists.Handlers
UploadIntentConfiguration uploadIntentConfiguration) UploadIntentConfiguration uploadIntentConfiguration)
: ICommandHandler<RequestPlayListPictureUploadCommand, Result<PlayListPictureUploadResponse>> : ICommandHandler<RequestPlayListPictureUploadCommand, Result<PlayListPictureUploadResponse>>
{ {
static readonly string[] AllowedExtensions = [".webp", ".png", ".jpg", ".jpeg"];
public async ValueTask<Result<PlayListPictureUploadResponse>> Handle(RequestPlayListPictureUploadCommand request, CancellationToken cancellationToken) public async ValueTask<Result<PlayListPictureUploadResponse>> Handle(RequestPlayListPictureUploadCommand request, CancellationToken cancellationToken)
{ {
var userExists = await database.Users var userExists = await database.Users
@@ -35,14 +32,10 @@ namespace Musify.Application.PlayLists.Handlers
return Result.NotFound($"User {request.UserId} not found"); return Result.NotFound($"User {request.UserId} not found");
} }
var extensionResult = UploadIntentHelpers.ValidateExtension(request.FileType, AllowedExtensions);
if (!extensionResult.IsSuccess)
return Result.Invalid(extensionResult.Errors.Select(error => new ValidationError(error)).ToArray());
var effectiveSizeBytes = request.ExpectedSizeBytes var effectiveSizeBytes = request.ExpectedSizeBytes
?? uploadIntentConfiguration.DefaultExpectedPictureSizeBytes; ?? uploadIntentConfiguration.DefaultExpectedPictureSizeBytes;
var objectName = Guid.NewGuid() + extensionResult.Value; var objectName = $"{Guid.NewGuid()}.{request.FileType.TrimStart('.').ToLowerInvariant()}";
var tempKey = playListConfiguration.Routes.BuildTempPicturePath( var tempKey = playListConfiguration.Routes.BuildTempPicturePath(
uploadIntentConfiguration.TempRootPrefix, request.UserId, objectName); uploadIntentConfiguration.TempRootPrefix, request.UserId, objectName);
@@ -59,11 +52,14 @@ namespace Musify.Application.PlayLists.Handlers
await using var transaction = await database.BeginTransactionAsync( await using var transaction = await database.BeginTransactionAsync(
System.Data.IsolationLevel.Serializable, cancellationToken); System.Data.IsolationLevel.Serializable, cancellationToken);
var quotaCheck = await uploadIntentService.CheckQuotaAsync( var quotaCheck = await uploadIntentValidator.CheckQuotaAsync(
uploadIntentConfiguration, logger, uploadIntentConfiguration,
request.UserId, effectiveSizeBytes, 1, cancellationToken); request.UserId, effectiveSizeBytes, 1, cancellationToken);
if (!quotaCheck.IsSuccess) if (!quotaCheck.IsSuccess)
{
logger.LogWarning("User {UserId} failed upload intent quota check", request.UserId);
return quotaCheck; return quotaCheck;
}
var intent = new UploadIntent var intent = new UploadIntent
{ {
@@ -3,13 +3,13 @@ using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Events; using Musify.Application.Events;
using Musify.Application.PlayLists.Commands; using Musify.Application.PlayLists.Commands;
using Musify.Application.PlayLists.Responses; using Musify.Application.PlayLists.Responses;
using Musify.Application.UploadIntents; using Musify.Application.UploadIntents;
using Musify.Application.Extensions; using Musify.Application.Extensions;
using Musify.Application.Abstractions.Application; using Musify.Application.Services;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Application.PlayLists.Handlers namespace Musify.Application.PlayLists.Handlers
@@ -17,7 +17,7 @@ namespace Musify.Application.PlayLists.Handlers
public class UpdatePlayListCommandHandler( public class UpdatePlayListCommandHandler(
IEventBus eventBus, IEventBus eventBus,
IDatabase database, IDatabase database,
IUploadIntentService uploadIntentService, UploadIntentValidator uploadIntentValidator,
IStorageService storageService, IStorageService storageService,
ILogger<UpdatePlayListCommandHandler> logger, ILogger<UpdatePlayListCommandHandler> logger,
ApplicationStorageConfiguration storageConfiguration, ApplicationStorageConfiguration storageConfiguration,
@@ -53,7 +53,7 @@ namespace Musify.Application.PlayLists.Handlers
if (request.NewPictureIntentId.HasValue) if (request.NewPictureIntentId.HasValue)
{ {
var validation = await uploadIntentService.ValidateAndLoadAsync( var validation = await uploadIntentValidator.ValidateAndLoadAsync(
uploadIntentConfiguration, uploadIntentConfiguration,
request.NewPictureIntentId.Value, request.UserId, cancellationToken); request.NewPictureIntentId.Value, request.UserId, cancellationToken);
if (!validation.IsSuccess) if (!validation.IsSuccess)
@@ -1,6 +1,6 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.PlayLists.Responses; using Musify.Application.PlayLists.Responses;
namespace Musify.Application.PlayLists.Queries namespace Musify.Application.PlayLists.Queries
@@ -1,6 +1,6 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.PlayLists.Responses; using Musify.Application.PlayLists.Responses;
namespace Musify.Application.PlayLists.Queries namespace Musify.Application.PlayLists.Queries
@@ -1,20 +1,17 @@
using Ardalis.Result; using Ardalis.Result;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Abstractions.Infrastructure;
using Musify.Application.Abstractions.Application;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Application.Services; namespace Musify.Application.Services;
public sealed class UploadIntentService( public sealed class UploadIntentValidator(
IDatabase database, IDatabase database,
IStorageService storageService) : IUploadIntentService IStorageService storageService)
{ {
public async Task<Result> CheckQuotaAsync( public async Task<Result> CheckQuotaAsync(
UploadIntentConfiguration config, UploadIntentConfiguration config,
ILogger logger,
Guid userId, Guid userId,
long requiredBytes, long requiredBytes,
int requiredIntentCount, int requiredIntentCount,
@@ -26,17 +23,11 @@ public sealed class UploadIntentService(
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
if (activeIntents.Count + requiredIntentCount > config.MaxActiveUploadIntentsPerUser) if (activeIntents.Count + requiredIntentCount > config.MaxActiveUploadIntentsPerUser)
{
logger.LogWarning("User {UserId} exceeded max active upload intents ({Max})", userId, config.MaxActiveUploadIntentsPerUser);
return Result.Invalid(new ValidationError("Upload intent limit exceeded. Wait for existing uploads to complete or expire.")); return Result.Invalid(new ValidationError("Upload intent limit exceeded. Wait for existing uploads to complete or expire."));
}
var activeBytes = activeIntents.Sum(i => i.ExpectedSizeBytes ?? config.DefaultExpectedPictureSizeBytes); var activeBytes = activeIntents.Sum(i => i.ExpectedSizeBytes ?? config.DefaultExpectedPictureSizeBytes);
if (activeBytes + requiredBytes > config.MaxActiveUploadBytesPerUser) if (activeBytes + requiredBytes > config.MaxActiveUploadBytesPerUser)
{
logger.LogWarning("User {UserId} exceeded max active upload bytes ({Max})", userId, config.MaxActiveUploadBytesPerUser);
return Result.Invalid(new ValidationError("Upload byte quota exceeded. Wait for existing uploads to complete or expire.")); return Result.Invalid(new ValidationError("Upload byte quota exceeded. Wait for existing uploads to complete or expire."));
}
return Result.Success(); return Result.Success();
} }
@@ -3,13 +3,13 @@ using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Events; using Musify.Application.Events;
using Musify.Application.Tracks.Commands; using Musify.Application.Tracks.Commands;
using Musify.Application.Tracks.Responses; using Musify.Application.Tracks.Responses;
using Musify.Application.UploadIntents; using Musify.Application.UploadIntents;
using Musify.Application.Extensions; using Musify.Application.Extensions;
using Musify.Application.Abstractions.Application; using Musify.Application.Services;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Application.Tracks.Handler namespace Musify.Application.Tracks.Handler
@@ -17,7 +17,7 @@ namespace Musify.Application.Tracks.Handler
public class CreateTrackCommandHandler( public class CreateTrackCommandHandler(
IDatabase database, IDatabase database,
IEventBus eventBus, IEventBus eventBus,
IUploadIntentService uploadIntentService, UploadIntentValidator uploadIntentValidator,
IStorageService storageService, IStorageService storageService,
ILogger<CreateTrackCommandHandler> logger, ILogger<CreateTrackCommandHandler> logger,
ApplicationStorageConfiguration storageConfiguration, ApplicationStorageConfiguration storageConfiguration,
@@ -35,13 +35,13 @@ namespace Musify.Application.Tracks.Handler
return Result.NotFound($"User {request.UserId} not found"); return Result.NotFound($"User {request.UserId} not found");
} }
var pictureValidation = await uploadIntentService.ValidateAndLoadAsync( var pictureValidation = await uploadIntentValidator.ValidateAndLoadAsync(
uploadIntentConfiguration, uploadIntentConfiguration,
request.PictureIntentId, request.UserId, cancellationToken); request.PictureIntentId, request.UserId, cancellationToken);
if (!pictureValidation.IsSuccess) if (!pictureValidation.IsSuccess)
return pictureValidation.As<UploadIntent, TrackApplicationResponse>(); return pictureValidation.As<UploadIntent, TrackApplicationResponse>();
var audioValidation = await uploadIntentService.ValidateAndLoadAsync( var audioValidation = await uploadIntentValidator.ValidateAndLoadAsync(
uploadIntentConfiguration, uploadIntentConfiguration,
request.AudioIntentId, request.UserId, cancellationToken); request.AudioIntentId, request.UserId, cancellationToken);
if (!audioValidation.IsSuccess) if (!audioValidation.IsSuccess)
@@ -2,7 +2,7 @@ using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Tracks.Commands; using Musify.Application.Tracks.Commands;
using Musify.Domain.Entities; using Musify.Domain.Entities;
using Musify.Application.Events; using Musify.Application.Events;
@@ -1,7 +1,7 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Tracks.Queries; using Musify.Application.Tracks.Queries;
using Musify.Application.Tracks.Responses; using Musify.Application.Tracks.Responses;
@@ -1,8 +1,8 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Tracks.Queries; using Musify.Application.Tracks.Queries;
using Musify.Application.Tracks.Responses; using Musify.Application.Tracks.Responses;
using X.PagedList.EF; using X.PagedList.EF;
@@ -1,8 +1,8 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Tracks.Queries; using Musify.Application.Tracks.Queries;
using Musify.Application.Tracks.Responses; using Musify.Application.Tracks.Responses;
using X.PagedList.EF; using X.PagedList.EF;
@@ -2,19 +2,18 @@ using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Application; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Abstractions.Infrastructure;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Application.Services;
using Musify.Application.Tracks.Commands; using Musify.Application.Tracks.Commands;
using Musify.Application.Tracks.Responses; using Musify.Application.Tracks.Responses;
using Musify.Application.UploadIntents;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Application.Tracks.Handler namespace Musify.Application.Tracks.Handler
{ {
public class RequestTrackUploadUrlsCommandHandler( public class RequestTrackUploadUrlsCommandHandler(
IDatabase database, IDatabase database,
IUploadIntentService uploadIntentService, UploadIntentValidator uploadIntentValidator,
IStorageService storageService, IStorageService storageService,
ILogger<RequestTrackUploadUrlsCommandHandler> logger, ILogger<RequestTrackUploadUrlsCommandHandler> logger,
ApplicationStorageConfiguration storageConfiguration, ApplicationStorageConfiguration storageConfiguration,
@@ -22,9 +21,6 @@ namespace Musify.Application.Tracks.Handler
UploadIntentConfiguration uploadIntentConfiguration) UploadIntentConfiguration uploadIntentConfiguration)
: ICommandHandler<RequestTrackUploadUrlsCommand, Result<TrackUploadUrlsResponse>> : ICommandHandler<RequestTrackUploadUrlsCommand, Result<TrackUploadUrlsResponse>>
{ {
static readonly string[] AllowedPictureExtensions = [".webp", ".png", ".jpg", ".jpeg"];
static readonly string[] AllowedAudioExtensions = [".mp3", ".wav", ".m4a", ".flac", ".ogg", ".aac", ".opus"];
public async ValueTask<Result<TrackUploadUrlsResponse>> Handle(RequestTrackUploadUrlsCommand request, CancellationToken cancellationToken) public async ValueTask<Result<TrackUploadUrlsResponse>> Handle(RequestTrackUploadUrlsCommand request, CancellationToken cancellationToken)
{ {
var userExists = await database.Users var userExists = await database.Users
@@ -36,21 +32,13 @@ namespace Musify.Application.Tracks.Handler
return Result.NotFound($"User {request.UserId} not found"); return Result.NotFound($"User {request.UserId} not found");
} }
var pictureExtensionResult = UploadIntentHelpers.ValidateExtension(request.PictureFileType, AllowedPictureExtensions);
if (!pictureExtensionResult.IsSuccess)
return Result.Invalid(pictureExtensionResult.Errors.Select(error => new ValidationError(error)).ToArray());
var audioExtensionResult = UploadIntentHelpers.ValidateExtension(request.AudioFileType, AllowedAudioExtensions);
if (!audioExtensionResult.IsSuccess)
return Result.Invalid(audioExtensionResult.Errors.Select(error => new ValidationError(error)).ToArray());
var effectivePictureSize = request.ExpectedPictureSizeBytes var effectivePictureSize = request.ExpectedPictureSizeBytes
?? uploadIntentConfiguration.DefaultExpectedPictureSizeBytes; ?? uploadIntentConfiguration.DefaultExpectedPictureSizeBytes;
var effectiveAudioSize = request.ExpectedAudioSizeBytes var effectiveAudioSize = request.ExpectedAudioSizeBytes
?? uploadIntentConfiguration.DefaultExpectedAudioSizeBytes; ?? uploadIntentConfiguration.DefaultExpectedAudioSizeBytes;
var pictureObjectName = Guid.NewGuid() + pictureExtensionResult.Value; var pictureObjectName = $"{Guid.NewGuid()}.{request.PictureFileType.TrimStart('.').ToLowerInvariant()}";
var audioObjectName = Guid.NewGuid() + audioExtensionResult.Value; var audioObjectName = $"{Guid.NewGuid()}.{request.AudioFileType.TrimStart('.').ToLowerInvariant()}";
var tempPictureKey = trackConfiguration.Routes.BuildTempPicturePath( var tempPictureKey = trackConfiguration.Routes.BuildTempPicturePath(
uploadIntentConfiguration.TempRootPrefix, request.UserId, pictureObjectName); uploadIntentConfiguration.TempRootPrefix, request.UserId, pictureObjectName);
@@ -80,11 +68,14 @@ namespace Musify.Application.Tracks.Handler
await using var transaction = await database.BeginTransactionAsync( await using var transaction = await database.BeginTransactionAsync(
System.Data.IsolationLevel.Serializable, cancellationToken); System.Data.IsolationLevel.Serializable, cancellationToken);
var quotaCheck = await uploadIntentService.CheckQuotaAsync( var quotaCheck = await uploadIntentValidator.CheckQuotaAsync(
uploadIntentConfiguration, logger, uploadIntentConfiguration,
request.UserId, effectivePictureSize + effectiveAudioSize, 2, cancellationToken); request.UserId, effectivePictureSize + effectiveAudioSize, 2, cancellationToken);
if (!quotaCheck.IsSuccess) if (!quotaCheck.IsSuccess)
{
logger.LogWarning("User {UserId} failed upload intent quota check", request.UserId);
return quotaCheck; return quotaCheck;
}
var pictureIntent = new UploadIntent var pictureIntent = new UploadIntent
{ {
@@ -1,6 +1,6 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.Tracks.Responses; using Musify.Application.Tracks.Responses;
namespace Musify.Application.Tracks.Queries namespace Musify.Application.Tracks.Queries
+2 -2
View File
@@ -1,6 +1,6 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.Tracks.Responses; using Musify.Application.Tracks.Responses;
namespace Musify.Application.Tracks.Queries namespace Musify.Application.Tracks.Queries
@@ -1,24 +0,0 @@
using Ardalis.Result;
namespace Musify.Application.UploadIntents
{
internal static class UploadIntentHelpers
{
internal static Result<string> ValidateExtension(string fileType, string[] allowedExtensions)
{
var extension = fileType.Trim();
if (!extension.StartsWith('.'))
extension = "." + extension;
extension = extension.ToLowerInvariant();
if (extension.Contains('/') || extension.Contains('\\'))
return Result.Error("Invalid FileType");
if (!allowedExtensions.Contains(extension))
return Result.Error($"Unsupported FileType '{extension}'");
return Result.Success(extension);
}
}
}
@@ -2,7 +2,7 @@ using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Users.Commands; using Musify.Application.Users.Commands;
using Musify.Application.Users.Responses; using Musify.Application.Users.Responses;
@@ -1,7 +1,7 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Users.Queries; using Musify.Application.Users.Queries;
using Musify.Application.Users.Responses; using Musify.Application.Users.Responses;
@@ -1,7 +1,7 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Users.Queries; using Musify.Application.Users.Queries;
using Musify.Application.Users.Responses; using Musify.Application.Users.Responses;
@@ -1,8 +1,8 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Users.Queries; using Musify.Application.Users.Queries;
using Musify.Application.Users.Responses; using Musify.Application.Users.Responses;
using X.PagedList.EF; using X.PagedList.EF;
+1 -1
View File
@@ -1,6 +1,6 @@
using Ardalis.Result; using Ardalis.Result;
using Mediator; using Mediator;
using Musify.Application.Abstractions.Application; using Musify.Application.Pagination;
using Musify.Application.Users.Responses; using Musify.Application.Users.Responses;
namespace Musify.Application.Users.Queries namespace Musify.Application.Users.Queries
+1
View File
@@ -11,4 +11,5 @@
<RepositoryUrl>https://github.com/XShine555/MusifyBackend</RepositoryUrl> <RepositoryUrl>https://github.com/XShine555/MusifyBackend</RepositoryUrl>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
+1 -1
View File
@@ -1,7 +1,7 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Configuration; using Musify.Application.Configuration;
namespace Musify.Infrastructure.Jobs namespace Musify.Infrastructure.Jobs
@@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Domain.Entities; using Musify.Domain.Entities;
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Domain.Entities; using Musify.Domain.Entities;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Domain.Entities; using Musify.Domain.Entities;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
using Musify.Infrastructure.MassTransit.Activities.Logs; using Musify.Infrastructure.MassTransit.Activities.Logs;
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Domain.Entities; using Musify.Domain.Entities;
using Musify.Infrastructure.MassTransit.Arguments; using Musify.Infrastructure.MassTransit.Arguments;
using Musify.Infrastructure.MassTransit.Logs; using Musify.Infrastructure.MassTransit.Logs;
@@ -1,6 +1,6 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
using Musify.Infrastructure.MassTransit.Activities.Logs; using Musify.Infrastructure.MassTransit.Activities.Logs;
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Amazon.S3; using Amazon.S3;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
using System.Net; using System.Net;
@@ -1,6 +1,6 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.MassTransit.Arguments; using Musify.Infrastructure.MassTransit.Arguments;
namespace Musify.Infrastructure.MassTransit.Activities.Files namespace Musify.Infrastructure.MassTransit.Activities.Files
@@ -1,6 +1,6 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
using Musify.Infrastructure.MassTransit.Activities.Logs; using Musify.Infrastructure.MassTransit.Activities.Logs;
@@ -1,6 +1,6 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.MassTransit.Arguments; using Musify.Infrastructure.MassTransit.Arguments;
using Musify.Infrastructure.MassTransit.Logs; using Musify.Infrastructure.MassTransit.Logs;
@@ -1,6 +1,6 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
using Musify.Infrastructure.MassTransit.Activities.Logs; using Musify.Infrastructure.MassTransit.Activities.Logs;
@@ -1,6 +1,6 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
using Musify.Infrastructure.MassTransit.Activities.Logs; using Musify.Infrastructure.MassTransit.Activities.Logs;
@@ -1,6 +1,6 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
using Musify.Infrastructure.MassTransit.Activities.Logs; using Musify.Infrastructure.MassTransit.Activities.Logs;
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
namespace Musify.Infrastructure.MassTransit.Activities.PlayLists namespace Musify.Infrastructure.MassTransit.Activities.PlayLists
{ {
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Infrastructure.MassTransit.Activities.PlayLists namespace Musify.Infrastructure.MassTransit.Activities.PlayLists
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
namespace Musify.Infrastructure.MassTransit.Activities.Tracks namespace Musify.Infrastructure.MassTransit.Activities.Tracks
{ {
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Domain.Entities; using Musify.Domain.Entities;
namespace Musify.Infrastructure.MassTransit.Activities.Tracks namespace Musify.Infrastructure.MassTransit.Activities.Tracks
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.Configuration; using Musify.Infrastructure.Configuration;
using Musify.Infrastructure.Persistence; using Musify.Infrastructure.Persistence;
@@ -1,5 +1,5 @@
using MassTransit; using MassTransit;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
namespace Musify.Infrastructure.MassTransit namespace Musify.Infrastructure.MassTransit
{ {
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using MassTransit.Courier.Contracts; using MassTransit.Courier.Contracts;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Infrastructure.MassTransit.Activities.Files; using Musify.Infrastructure.MassTransit.Activities.Files;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
@@ -1,7 +1,7 @@
using MassTransit; using MassTransit;
using MassTransit.Courier.Contracts; using MassTransit.Courier.Contracts;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Infrastructure.MassTransit.Activities.Files; using Musify.Infrastructure.MassTransit.Activities.Files;
using Musify.Infrastructure.MassTransit.Activities.Arguments; using Musify.Infrastructure.MassTransit.Activities.Arguments;
+2 -2
View File
@@ -1,10 +1,10 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using MassTransit; using MassTransit;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Domain.Entities; using Musify.Domain.Entities;
using Musify.Infrastructure.Configuration; using Musify.Infrastructure.Configuration;
using AppIDatabase = Musify.Application.Abstractions.Infrastructure.IDatabase; using AppIDatabase = Musify.Application.Contracts.Infrastructure.IDatabase;
namespace Musify.Infrastructure.Persistence namespace Musify.Infrastructure.Persistence
{ {
@@ -1,7 +1,7 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.Configuration; using Musify.Infrastructure.Configuration;
namespace Musify.Infrastructure.Persistence namespace Musify.Infrastructure.Persistence
@@ -1,5 +1,5 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Infrastructure.Configuration; using Musify.Infrastructure.Configuration;
using System.Diagnostics; using System.Diagnostics;
+1 -1
View File
@@ -1,5 +1,5 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
@@ -1,9 +1,8 @@
using Amazon.S3; using Amazon.S3;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Application.Abstractions.Application;
using Musify.Application.Configuration; using Musify.Application.Configuration;
using Musify.Infrastructure.Configuration; using Musify.Infrastructure.Configuration;
using Musify.Infrastructure.Jobs; using Musify.Infrastructure.Jobs;
@@ -56,7 +55,7 @@ namespace Musify.Infrastructure.Services
serviceDescriptors.AddSingleton(serviceProvider => serviceDescriptors.AddSingleton(serviceProvider =>
serviceProvider.GetRequiredService<IOptions<UploadIntentConfiguration>>().Value); serviceProvider.GetRequiredService<IOptions<UploadIntentConfiguration>>().Value);
serviceDescriptors.AddScoped<IUploadIntentService, UploadIntentService>(); serviceDescriptors.AddScoped<UploadIntentValidator>();
return serviceDescriptors; return serviceDescriptors;
} }
+2 -2
View File
@@ -3,10 +3,10 @@ using Amazon.S3.Model;
using Amazon.S3.Transfer; using Amazon.S3.Transfer;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MimeMapping; using MimeMapping;
using Musify.Application.Abstractions.Infrastructure; using Musify.Application.Contracts.Infrastructure;
using Musify.Domain.Entities; using Musify.Domain.Entities;
using Musify.Infrastructure.Configuration; using Musify.Infrastructure.Configuration;
using ObjectMetadata = Musify.Application.Abstractions.Infrastructure.ObjectMetadata; using ObjectMetadata = Musify.Application.Contracts.Infrastructure.ObjectMetadata;
namespace Musify.Infrastructure.Services namespace Musify.Infrastructure.Services
{ {