Reorganize backend to mirror Musify.slnx folder structure
Moves the six backend projects on disk into Core/ and Hosts/ subfolders, matching the solution folders already defined in Musify.slnx, so the file explorer reflects the same layout as the solution: backend/Core/Musify.Domain backend/Core/Musify.Application backend/Core/Musify.Infrastructure backend/Hosts/Musify.Api backend/Hosts/Musify.Worker backend/Hosts/Musify.StreamingGateway Updates every reference to the old paths: ProjectReference entries in Musify.Api.csproj and Musify.Worker.csproj, the three Dockerfiles, deploy/scripts/migrate.ps1|sh, deploy/zitadel/provision.ps1, deploy/up.ps1, deploy/README.md and docs/development.md. Also fixes a stale Dockerfile comment pointing at a compose file that no longer exists. Verified with `dotnet build Musify.slnx` against the new layout.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
using ErrorOr;
|
||||
using Mediator;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Musify.Application.Configuration;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Events;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Application.Services;
|
||||
using Musify.Domain.Entities;
|
||||
using Musify.Domain.ValueObjects;
|
||||
using Musify.Application.Contracts;
|
||||
|
||||
namespace Musify.Application.Tracks
|
||||
{
|
||||
public record CreateTrackCommand(
|
||||
long UserId,
|
||||
string Title,
|
||||
Guid PictureIntentId,
|
||||
Guid AudioIntentId)
|
||||
: ICommand<ErrorOr<TrackApplicationResponse>>;
|
||||
|
||||
public class CreateTrackCommandHandler(
|
||||
IDatabase database,
|
||||
IEventBus eventBus,
|
||||
UploadIntentValidator uploadIntentValidator,
|
||||
ILogger<CreateTrackCommandHandler> logger,
|
||||
ApplicationStorageConfiguration storageConfiguration,
|
||||
TrackConfiguration trackConfiguration,
|
||||
UploadIntentConfiguration uploadIntentConfiguration)
|
||||
: ICommandHandler<CreateTrackCommand, ErrorOr<TrackApplicationResponse>>
|
||||
{
|
||||
public async ValueTask<ErrorOr<TrackApplicationResponse>> Handle(CreateTrackCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await database.Users
|
||||
.SingleOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
||||
if (user is null)
|
||||
{
|
||||
logger.LogWarning("User {UserId} not found", request.UserId);
|
||||
return Error.NotFound(description: $"User {request.UserId} not found");
|
||||
}
|
||||
|
||||
var pictureValidation = await uploadIntentValidator.ValidateAndLoadAsync(
|
||||
uploadIntentConfiguration,
|
||||
request.PictureIntentId, request.UserId, cancellationToken);
|
||||
if (pictureValidation.IsError)
|
||||
return pictureValidation.Errors;
|
||||
|
||||
var audioValidation = await uploadIntentValidator.ValidateAndLoadAsync(
|
||||
uploadIntentConfiguration,
|
||||
request.AudioIntentId, request.UserId, cancellationToken);
|
||||
if (audioValidation.IsError)
|
||||
return audioValidation.Errors;
|
||||
|
||||
var pictureIntent = pictureValidation.Value;
|
||||
var audioIntent = audioValidation.Value;
|
||||
|
||||
var finalPictureKey = trackConfiguration.Routes.BuildOriginalPicturePath(request.UserId, pictureIntent.ObjectName);
|
||||
var finalAudioKey = trackConfiguration.Routes.BuildOriginalAudioPath(request.UserId, audioIntent.ObjectName);
|
||||
var audioProcessedFolderKey = trackConfiguration.Routes.BuildProcessedAudioPath(Guid.NewGuid().ToString());
|
||||
|
||||
var trackEntity = new LocalTrack
|
||||
{
|
||||
Title = request.Title,
|
||||
NormalizedTitle = request.Title.ToUpperInvariant(),
|
||||
OwnerUserId = user.Id,
|
||||
Owner = user,
|
||||
Pictures = new TrackPictures
|
||||
{
|
||||
OriginalName = pictureIntent.ObjectName,
|
||||
SmallName = trackConfiguration.Routes.PresetSmallPicture,
|
||||
MediumName = trackConfiguration.Routes.PresetMediumPicture,
|
||||
LargeName = trackConfiguration.Routes.PresetLargePicture,
|
||||
ProcessingStatus = ProcessingStatus.Pending
|
||||
},
|
||||
Audio = new TrackAudio
|
||||
{
|
||||
OriginalName = audioIntent.ObjectName,
|
||||
TranscodeStatus = ProcessingStatus.Pending
|
||||
}
|
||||
};
|
||||
|
||||
await database.LocalTracks.AddAsync(trackEntity, cancellationToken);
|
||||
|
||||
await database.UserHasTracks.AddAsync(new UserHasTrack
|
||||
{
|
||||
UserId = request.UserId,
|
||||
TrackId = trackEntity.Id
|
||||
}, cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
await eventBus.PublishAsync(
|
||||
new CreateTrackResourcesEvent(
|
||||
trackEntity.Id,
|
||||
pictureIntent.Id,
|
||||
audioIntent.Id,
|
||||
storageConfiguration.Bucket,
|
||||
pictureIntent.Key,
|
||||
finalPictureKey,
|
||||
audioIntent.Key,
|
||||
finalAudioKey,
|
||||
audioProcessedFolderKey,
|
||||
new ImageSize(
|
||||
trackConfiguration.Routes.SmallPicturesPath,
|
||||
trackConfiguration.PicturesSizes.SmallPictureWidth,
|
||||
trackConfiguration.PicturesSizes.SmallPictureHeight),
|
||||
new ImageSize(
|
||||
trackConfiguration.Routes.MediumPicturesPath,
|
||||
trackConfiguration.PicturesSizes.MediumPictureWidth,
|
||||
trackConfiguration.PicturesSizes.MediumPictureHeight),
|
||||
new ImageSize(
|
||||
trackConfiguration.Routes.LargePicturesPath,
|
||||
trackConfiguration.PicturesSizes.LargePictureWidth,
|
||||
trackConfiguration.PicturesSizes.LargePictureHeight)),
|
||||
cancellationToken);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.LogError(exception, "Failed to publish create track event for track {TrackId}", trackEntity.Id);
|
||||
return Error.Failure(description: $"Failed to create track for {request.Title}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await database.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.LogError(exception, "Failed to save track {Title}", request.Title);
|
||||
return Error.Failure(description: $"Failed to create track for {request.Title}");
|
||||
}
|
||||
|
||||
return TrackApplicationResponse.FromEntity(trackEntity, listensCount: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user