Refactor Dockerfile and documentation for non-root user setup. Update CLAUDE.md and README.md to clarify media storage permissions and user requirements. Modify appsettings for local development database configuration. Enhance MediaEndpoints by removing unused regex and improving file resolution logic. Update BumperPreview to use SHA-256 for asset ID generation, ensuring better security practices.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using LiteCqrs;
|
||||
using Microsoft.Extensions.Options;
|
||||
using TeleWave.Api.Common;
|
||||
@@ -19,8 +18,6 @@ namespace TeleWave.Api.Endpoints;
|
||||
|
||||
public static class MediaEndpoints
|
||||
{
|
||||
private static readonly Regex SegmentFileName = new(@"^seg\d{1,6}\.ts$", RegexOptions.Compiled);
|
||||
|
||||
public static IEndpointRouteBuilder MapMediaEndpoints(this IEndpointRouteBuilder app)
|
||||
{
|
||||
var admin = app.MapGroup("/api/admin/media")
|
||||
@@ -177,16 +174,7 @@ public static class MediaEndpoints
|
||||
/// <summary>Плейлист ассета: переписываем ffmpeg-index.m3u8, направляя сегменты на admin-роут.</summary>
|
||||
private static IResult PreviewPlaylist(Guid id, MediaPathResolver paths)
|
||||
{
|
||||
string indexPath;
|
||||
try
|
||||
{
|
||||
indexPath = paths.SegmentPath(id, "index.m3u8");
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return Results.NotFound();
|
||||
}
|
||||
if (!File.Exists(indexPath))
|
||||
if (SegmentFiles.TryResolveExisting(paths, id, "index.m3u8") is not { } indexPath)
|
||||
return Results.NotFound();
|
||||
|
||||
var baseUrl = $"/api/admin/media/{id}/preview/";
|
||||
@@ -206,19 +194,10 @@ public static class MediaEndpoints
|
||||
|
||||
private static IResult PreviewSegment(Guid id, string file, MediaPathResolver paths)
|
||||
{
|
||||
if (!SegmentFileName.IsMatch(file))
|
||||
if (!SegmentFiles.IsSegmentName(file))
|
||||
return Results.NotFound();
|
||||
|
||||
string path;
|
||||
try
|
||||
{
|
||||
path = paths.SegmentPath(id, file);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return Results.NotFound();
|
||||
}
|
||||
if (!File.Exists(path))
|
||||
if (SegmentFiles.TryResolveExisting(paths, id, file) is not { } path)
|
||||
return Results.NotFound();
|
||||
|
||||
return Results.File(path, "video/mp2t", enableRangeProcessing: true);
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
{
|
||||
// Локальная БД разработчика. В appsettings.json строки подключения нет намеренно: этот файл
|
||||
// едет в образ, и креды в нём (даже заведомо игрушечные) — это и находка сканера, и приглашение
|
||||
// однажды поправить их «на месте» вместо ConnectionStrings__Default из окружения.
|
||||
"ConnectionStrings": {
|
||||
"Default": "Host=localhost;Port=5432;Database=telewave;Username=telewave;Password=telewave"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"Default": "Host=localhost;Port=5432;Database=telewave;Username=telewave;Password=telewave"
|
||||
},
|
||||
"Jwt": {
|
||||
"Issuer": "TeleWave",
|
||||
"Audience": "TeleWave",
|
||||
|
||||
@@ -3,10 +3,16 @@ using System.Security.Cryptography;
|
||||
namespace TeleWave.Application.Broadcast.Bumpers;
|
||||
|
||||
/// <summary>
|
||||
/// Детерминированный id ассета-превью для блока заставки: один и тот же на каждый повторный рендер,
|
||||
/// Детерминированный id ассета-превью для подблока заставки: один и тот же на каждый повторный рендер,
|
||||
/// поэтому предпросмотр перезаписывает единственный каталог assets/{id}, а не плодит новые.
|
||||
/// </summary>
|
||||
public static class BumperPreview
|
||||
{
|
||||
public static Guid AssetId(Guid templateId) => new(MD5.HashData(templateId.ToByteArray()));
|
||||
/// <summary>
|
||||
/// Свёртка id подблока в стабильный id превью. Не защита — просто способ получить из одного GUID
|
||||
/// другой, воспроизводимо; берём SHA-256 и первые 16 байт, чтобы в коде не оставалось вызовов
|
||||
/// сломанных хеш-функций, которые потом приходится каждый раз объяснять сканерам.
|
||||
/// </summary>
|
||||
public static Guid AssetId(Guid variantId) =>
|
||||
new(SHA256.HashData(variantId.ToByteArray()).AsSpan(0, 16));
|
||||
}
|
||||
|
||||
@@ -7,20 +7,28 @@ namespace TeleWave.Application.Library;
|
||||
/// вытаскиваются из имён по мере надобности (сводка в списке шоу, метка в расписании админки).</summary>
|
||||
public static class EpisodeName
|
||||
{
|
||||
/// <summary>Потолок на один Match. Шаблоны ниже линейные и в катастрофический бэктрекинг не уходят,
|
||||
/// но имя файла приходит извне (загрузка, inbox), а разбор идёт в цикле по всей библиотеке —
|
||||
/// страховка стоит ноль, а её отсутствие превращает любую будущую правку шаблона в риск.</summary>
|
||||
private static readonly TimeSpan MatchTimeout = TimeSpan.FromSeconds(1);
|
||||
|
||||
private static readonly Regex SxxEyy = new(
|
||||
@"[Ss](\d{1,2})[ ._-]*[Ee](\d{1,3})",
|
||||
RegexOptions.Compiled
|
||||
RegexOptions.Compiled,
|
||||
MatchTimeout
|
||||
);
|
||||
|
||||
private static readonly Regex NxNN = new(
|
||||
@"(?:^|[^\d])(\d{1,2})x(\d{1,3})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase,
|
||||
MatchTimeout
|
||||
);
|
||||
|
||||
// Ведущий номер серии: «01. Название», «02 - Название», «03_Название» — сезон считаем первым.
|
||||
private static readonly Regex LeadingNumber = new(
|
||||
@"^\s*(\d{1,3})[\s._)\]-]",
|
||||
RegexOptions.Compiled
|
||||
RegexOptions.Compiled,
|
||||
MatchTimeout
|
||||
);
|
||||
|
||||
public static (int Season, int Episode)? Parse(string? name)
|
||||
|
||||
Reference in New Issue
Block a user