Add maintenance management features: implement maintenance endpoints in the API, enhance media upload handling to skip duplicate files, and update frontend routes and translations for maintenance operations.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
using LiteCqrs;
|
||||
using TeleWave.Application.Common.Models;
|
||||
|
||||
namespace TeleWave.Application.Maintenance.ClearAllMedia;
|
||||
|
||||
/// <summary>Удаляет ВСЕ медиа-ассеты (строки + файлы) и связанное расписание. Опасная операция.</summary>
|
||||
public sealed record ClearAllMediaCommand : ICommand<Result<int>>;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using LiteCqrs;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TeleWave.Application.Common.Interfaces;
|
||||
using TeleWave.Application.Common.Models;
|
||||
|
||||
namespace TeleWave.Application.Maintenance.ClearAllMedia;
|
||||
|
||||
public sealed class ClearAllMediaCommandHandler(IAppDbContext dbContext, IMediaStorage storage)
|
||||
: ICommandHandler<ClearAllMediaCommand, Result<int>>
|
||||
{
|
||||
public async Task<Result<int>> Handle(
|
||||
ClearAllMediaCommand command,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var assets = await dbContext.MediaAssets
|
||||
.Select(a => new { a.Id, a.OriginalExtension })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var asset in assets)
|
||||
storage.DeleteAssetArtifacts(asset.Id, asset.OriginalExtension);
|
||||
|
||||
// Расписание ссылается на удаляемые ассеты — чистим его тоже.
|
||||
await dbContext.ScheduleEntries.ExecuteDeleteAsync(cancellationToken);
|
||||
var deleted = await dbContext.MediaAssets.ExecuteDeleteAsync(cancellationToken);
|
||||
|
||||
return Result.Success(deleted);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using LiteCqrs;
|
||||
using TeleWave.Application.Common.Models;
|
||||
|
||||
namespace TeleWave.Application.Maintenance.DeleteAllShows;
|
||||
|
||||
/// <summary>Удаляет ВСЕ шоу (с сериями через каскад). Медиа-ассеты остаются в библиотеке.</summary>
|
||||
public sealed record DeleteAllShowsCommand : ICommand<Result<int>>;
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
using LiteCqrs;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TeleWave.Application.Common.Interfaces;
|
||||
using TeleWave.Application.Common.Models;
|
||||
|
||||
namespace TeleWave.Application.Maintenance.DeleteAllShows;
|
||||
|
||||
public sealed class DeleteAllShowsCommandHandler(IAppDbContext dbContext)
|
||||
: ICommandHandler<DeleteAllShowsCommand, Result<int>>
|
||||
{
|
||||
public async Task<Result<int>> Handle(
|
||||
DeleteAllShowsCommand command,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
// Серии удаляются каскадом (FK Show → ShowEpisode: OnDelete Cascade).
|
||||
var deleted = await dbContext.Shows.ExecuteDeleteAsync(cancellationToken);
|
||||
return Result.Success(deleted);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using LiteCqrs;
|
||||
using TeleWave.Application.Common.Models;
|
||||
|
||||
namespace TeleWave.Application.Maintenance.DeleteShowMedia;
|
||||
|
||||
/// <summary>Удаляет медиа-ассеты (файлы + строки) всех серий указанного шоу и очищает его серии.</summary>
|
||||
public sealed record DeleteShowMediaCommand(Guid ShowId) : ICommand<Result<int>>;
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
using LiteCqrs;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TeleWave.Application.Common.Interfaces;
|
||||
using TeleWave.Application.Common.Models;
|
||||
using TeleWave.Application.Library;
|
||||
|
||||
namespace TeleWave.Application.Maintenance.DeleteShowMedia;
|
||||
|
||||
public sealed class DeleteShowMediaCommandHandler(IAppDbContext dbContext, IMediaStorage storage)
|
||||
: ICommandHandler<DeleteShowMediaCommand, Result<int>>
|
||||
{
|
||||
public async Task<Result<int>> Handle(
|
||||
DeleteShowMediaCommand command,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var show = await dbContext.Shows
|
||||
.Include(s => s.Episodes)
|
||||
.FirstOrDefaultAsync(s => s.Id == command.ShowId, cancellationToken);
|
||||
if (show is null)
|
||||
return Result.Failure<int>(ShowErrors.NotFound);
|
||||
|
||||
var episodes = show.Episodes.ToList();
|
||||
var assetIds = episodes.Select(e => e.MediaAssetId).Distinct().ToList();
|
||||
|
||||
var assets = await dbContext.MediaAssets
|
||||
.Where(a => assetIds.Contains(a.Id))
|
||||
.Select(a => new { a.Id, a.OriginalExtension })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var asset in assets)
|
||||
storage.DeleteAssetArtifacts(asset.Id, asset.OriginalExtension);
|
||||
|
||||
await dbContext.ScheduleEntries
|
||||
.Where(e => assetIds.Contains(e.MediaAssetId))
|
||||
.ExecuteDeleteAsync(cancellationToken);
|
||||
await dbContext.MediaAssets
|
||||
.Where(a => assetIds.Contains(a.Id))
|
||||
.ExecuteDeleteAsync(cancellationToken);
|
||||
|
||||
// Серии шоу теперь указывают на удалённые ассеты — убираем их (сохранится через UnitOfWork).
|
||||
foreach (var episode in episodes)
|
||||
show.RemoveEpisode(episode.Id);
|
||||
|
||||
return Result.Success(assets.Count);
|
||||
}
|
||||
}
|
||||
@@ -33,4 +33,9 @@ public static class MediaErrors
|
||||
"Media.SourceNotFound",
|
||||
"Исходный файл не найден в хранилище."
|
||||
);
|
||||
|
||||
public static readonly Error DuplicateFileName = Error.Conflict(
|
||||
"Media.DuplicateFileName",
|
||||
"Файл с таким именем уже загружен."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using LiteCqrs;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TeleWave.Application.Common.Interfaces;
|
||||
using TeleWave.Application.Common.Models;
|
||||
using TeleWave.Domain.Media;
|
||||
@@ -15,6 +16,16 @@ public sealed class RegisterMediaAssetCommandHandler(
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
// Дедуп по имени: тот же файл уже в библиотеке (кроме проваленных — их разрешаем перезалить).
|
||||
var duplicate = await dbContext.MediaAssets.AnyAsync(
|
||||
x =>
|
||||
x.OriginalFileName == command.OriginalFileName
|
||||
&& x.Status != MediaAssetStatus.Failed,
|
||||
cancellationToken
|
||||
);
|
||||
if (duplicate)
|
||||
return Result.Failure<Guid>(MediaErrors.DuplicateFileName);
|
||||
|
||||
var extension = Path.GetExtension(command.OriginalFileName).ToLowerInvariant();
|
||||
var asset = MediaAsset.Register(command.OriginalFileName, extension, command.Source);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user