141 lines
4.8 KiB
C#
141 lines
4.8 KiB
C#
using LiteCqrs;
|
|
using Microsoft.Extensions.Options;
|
|
using TeleWave.Api.Common;
|
|
using TeleWave.Application.Common.Interfaces;
|
|
using TeleWave.Application.Common.Models;
|
|
using TeleWave.Application.Media;
|
|
using TeleWave.Application.Media.Delete;
|
|
using TeleWave.Application.Media.GetMedia;
|
|
using TeleWave.Application.Media.ListMedia;
|
|
using TeleWave.Application.Media.Register;
|
|
using TeleWave.Domain.Media;
|
|
using TeleWave.Infrastructure.Identity;
|
|
using TeleWave.Infrastructure.Media;
|
|
|
|
namespace TeleWave.Api.Endpoints;
|
|
|
|
public static class MediaEndpoints
|
|
{
|
|
public static IEndpointRouteBuilder MapMediaEndpoints(this IEndpointRouteBuilder app)
|
|
{
|
|
var admin = app.MapGroup("/api/admin/media")
|
|
.WithTags("Admin.Media")
|
|
.RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin));
|
|
|
|
admin.MapPost("", Upload).Produces<UploadMediaResponse>(StatusCodes.Status201Created);
|
|
admin.MapGet("", List).Produces<PagedList<MediaAssetDto>>();
|
|
admin.MapGet("/{id:guid}", Get).Produces<MediaAssetDto>();
|
|
admin.MapDelete("/{id:guid}", Delete).Produces(StatusCodes.Status204NoContent);
|
|
|
|
return app;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Потоковая загрузка: тело запроса — сырые байты файла, имя передаётся в query «fileName».
|
|
/// Файл стримится на диск без буферизации в память, затем регистрируется и уходит в обработку.
|
|
/// </summary>
|
|
private static async Task<IResult> Upload(
|
|
string fileName,
|
|
HttpRequest request,
|
|
IMediaStorage storage,
|
|
IMediaProcessingQueue queue,
|
|
ISender sender,
|
|
IOptions<MediaOptions> mediaOptions,
|
|
IOptions<StorageOptions> storageOptions,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(fileName))
|
|
return Results.Problem(
|
|
title: MediaErrors.EmptyFileName.Code,
|
|
detail: MediaErrors.EmptyFileName.Message,
|
|
statusCode: StatusCodes.Status400BadRequest
|
|
);
|
|
|
|
if (!MediaFormats.IsAllowed(fileName))
|
|
return Results.Problem(
|
|
title: MediaErrors.UnsupportedFormat.Code,
|
|
detail: MediaErrors.UnsupportedFormat.Message,
|
|
statusCode: StatusCodes.Status400BadRequest
|
|
);
|
|
|
|
var contentLength = request.ContentLength ?? 0;
|
|
if (contentLength > mediaOptions.Value.MaxUploadBytes)
|
|
return Results.Problem(
|
|
title: MediaErrors.FileTooLarge.Code,
|
|
detail: MediaErrors.FileTooLarge.Message,
|
|
statusCode: StatusCodes.Status400BadRequest
|
|
);
|
|
|
|
var free = storage.GetAvailableFreeSpaceBytes();
|
|
if (free - contentLength < storageOptions.Value.MinFreeSpaceBytes)
|
|
return Results.Problem(
|
|
title: MediaErrors.InsufficientStorage.Code,
|
|
detail: MediaErrors.InsufficientStorage.Message,
|
|
statusCode: StatusCodes.Status409Conflict
|
|
);
|
|
|
|
var extension = Path.GetExtension(fileName);
|
|
var token = await storage.SaveUploadAsync(request.Body, extension, cancellationToken);
|
|
|
|
var result = await sender.Send(
|
|
new RegisterMediaAssetCommand(token, MediaSource.Upload, fileName),
|
|
cancellationToken
|
|
);
|
|
if (!result.IsSuccess)
|
|
{
|
|
storage.DeleteUpload(token);
|
|
return result.ToHttpResult();
|
|
}
|
|
|
|
queue.Enqueue(result.Value);
|
|
return Results.Created(
|
|
$"/api/admin/media/{result.Value}",
|
|
new UploadMediaResponse(result.Value)
|
|
);
|
|
}
|
|
|
|
private static async Task<IResult> List(
|
|
int page,
|
|
int pageSize,
|
|
MediaAssetStatus[]? status,
|
|
string? search,
|
|
ISender sender,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
var result = await sender.Send(
|
|
new ListMediaAssetsQuery(
|
|
page <= 0 ? 1 : page,
|
|
pageSize <= 0 ? 20 : pageSize,
|
|
status ?? [],
|
|
search
|
|
),
|
|
cancellationToken
|
|
);
|
|
return Results.Ok(result);
|
|
}
|
|
|
|
private static async Task<IResult> Get(
|
|
Guid id,
|
|
ISender sender,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
var result = await sender.Send(new GetMediaAssetQuery(id), cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
|
|
private static async Task<IResult> Delete(
|
|
Guid id,
|
|
ISender sender,
|
|
CancellationToken cancellationToken
|
|
)
|
|
{
|
|
var result = await sender.Send(new DeleteMediaAssetCommand(id), cancellationToken);
|
|
return result.ToHttpResult();
|
|
}
|
|
}
|
|
|
|
public sealed record UploadMediaResponse(Guid Id);
|