Implement media storage and processing features: update configuration in .env.example and appsettings files, enhance Docker setup for media storage, and add media-related services and database entities. Include ffmpeg for media handling and adjust Kestrel settings for large file uploads.

This commit is contained in:
Leonid Pershin
2026-07-24 08:33:11 +03:00
parent 1dd6991174
commit e15ecbdb29
47 changed files with 2599 additions and 1 deletions
@@ -0,0 +1,70 @@
using Microsoft.Extensions.Options;
namespace TeleWave.Infrastructure.Media;
/// <summary>
/// Единая точка резолва путей хранилища + защита от path traversal. Любой путь, собранный из
/// внешних данных (имя загруженного файла, имя из inbox/), проверяется на нахождение внутри корня.
/// </summary>
public sealed class MediaPathResolver
{
private readonly string _root;
public MediaPathResolver(IOptions<StorageOptions> options)
{
_root = Path.GetFullPath(options.Value.RootPath);
InboxDir = Path.Combine(_root, "inbox");
UploadsDir = Path.Combine(_root, "uploads");
OriginalsDir = Path.Combine(_root, "originals");
AssetsDir = Path.Combine(_root, "assets");
}
public string InboxDir { get; }
public string UploadsDir { get; }
public string OriginalsDir { get; }
public string AssetsDir { get; }
public void EnsureDirectories()
{
Directory.CreateDirectory(InboxDir);
Directory.CreateDirectory(UploadsDir);
Directory.CreateDirectory(OriginalsDir);
Directory.CreateDirectory(AssetsDir);
}
public string OriginalPath(Guid assetId, string extension) =>
EnsureWithinRoot(Path.Combine(OriginalsDir, assetId.ToString("N") + extension));
public string AssetDir(Guid assetId) =>
EnsureWithinRoot(Path.Combine(AssetsDir, assetId.ToString("N")));
public string AssetRelativePath(Guid assetId) => $"assets/{assetId:N}";
/// <summary>Резолвит имя файла внутри uploads/ (токен загрузки), проверяя выход за пределы каталога.</summary>
public string UploadPath(string token) =>
EnsureWithin(UploadsDir, Path.Combine(UploadsDir, token));
/// <summary>Резолвит имя файла внутри inbox/, проверяя выход за пределы каталога.</summary>
public string InboxPath(string fileName) =>
EnsureWithin(InboxDir, Path.Combine(InboxDir, fileName));
private string EnsureWithinRoot(string candidate) => EnsureWithin(_root, candidate);
private static string EnsureWithin(string baseDir, string candidate)
{
var full = Path.GetFullPath(candidate);
var normalizedBase = baseDir.EndsWith(Path.DirectorySeparatorChar)
? baseDir
: baseDir + Path.DirectorySeparatorChar;
if (
!full.StartsWith(normalizedBase, StringComparison.Ordinal)
&& !string.Equals(full, baseDir, StringComparison.Ordinal)
)
throw new UnauthorizedAccessException(
$"Путь '{candidate}' выходит за пределы каталога хранилища."
);
return full;
}
}