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,63 @@
|
||||
using ErrorOr;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Musify.Application.Configuration;
|
||||
using Musify.Domain.Entities;
|
||||
using Musify.Domain.ValueObjects;
|
||||
using Musify.Application.Contracts;
|
||||
|
||||
namespace Musify.Application.Services;
|
||||
|
||||
public sealed class UploadIntentValidator(
|
||||
IDatabase database,
|
||||
IStorageService storageService)
|
||||
{
|
||||
public async Task<ErrorOr<Success>> CheckQuotaAsync(
|
||||
UploadIntentConfiguration config,
|
||||
long userId,
|
||||
long requiredBytes,
|
||||
int requiredIntentCount,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var activeIntents = await database.UploadIntents
|
||||
.AsNoTracking()
|
||||
.Where(i => i.UserId == userId && i.Status == UploadIntentStatus.Issued)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (activeIntents.Count + requiredIntentCount > config.MaxActiveUploadIntentsPerUser)
|
||||
return Error.Validation(description: "Upload intent limit exceeded. Wait for existing uploads to complete or expire.");
|
||||
|
||||
var activeBytes = activeIntents.Sum(i => i.ExpectedSizeBytes ?? config.DefaultExpectedPictureSizeBytes);
|
||||
if (activeBytes + requiredBytes > config.MaxActiveUploadBytesPerUser)
|
||||
return Error.Validation(description: "Upload byte quota exceeded. Wait for existing uploads to complete or expire.");
|
||||
|
||||
return new Success();
|
||||
}
|
||||
|
||||
public async Task<ErrorOr<UploadIntent>> ValidateAndLoadAsync(
|
||||
UploadIntentConfiguration config,
|
||||
Guid intentId,
|
||||
long userId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var intent = await database.UploadIntents
|
||||
.FirstOrDefaultAsync(i => i.Id == intentId, cancellationToken);
|
||||
|
||||
if (intent is null || intent.UserId != userId)
|
||||
return Error.NotFound(description: "Upload intent not found or not accessible.");
|
||||
|
||||
if (intent.IsConsumed)
|
||||
return Error.Conflict(description: "Upload intent has already been consumed.");
|
||||
|
||||
if (intent.IsExpired)
|
||||
return Error.Validation(description: "Upload intent has expired.");
|
||||
|
||||
var metadata = await storageService.HeadObjectAsync(intent.Bucket, intent.Key, cancellationToken);
|
||||
if (metadata is null)
|
||||
return Error.NotFound(description: "Uploaded object not found in storage. Upload the file first.");
|
||||
|
||||
if (metadata.ContentLength > config.MaxUploadBytes)
|
||||
return Error.Validation(description: $"Uploaded file exceeds the maximum allowed size of {config.MaxUploadBytes} bytes.");
|
||||
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user