Структура, доп модули, федерация, документация

This commit is contained in:
Халимов Рустам
2026-03-27 00:55:01 +03:00
parent 7cb6ac61dd
commit 7ef73b414c
64 changed files with 3080 additions and 133 deletions

View File

@@ -6,6 +6,10 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Mvc;
using Knot.Shared.Kernel.Configuration;
using System.Text;
using System.Security.Cryptography;
namespace Knot.Host.Presentation.Endpoints;
public sealed class FilesEndpoints : ICarterModule
@@ -16,6 +20,7 @@ public sealed class FilesEndpoints : ICarterModule
group.MapGet("{id}", async (string id, [FromQuery] bool download, IFileStorageService fileStorage, IMemoryCache cache, CancellationToken ct) =>
{
// (Текущая логика локальных файлов остается без изменений)
try
{
var cacheKey = $"file_{id}";
@@ -49,5 +54,34 @@ public sealed class FilesEndpoints : ICarterModule
return Results.NotFound(new { error = "Файл не найден или ошибка доступа.", message = ex.Message });
}
});
// Новый эндпоинт для удаленных (федеративных) файлов
group.MapGet("/remote/{domain}/{id}", async (string domain, string id, HttpClient httpClient, ISettingsService settingsService, CancellationToken ct) =>
{
var settings = await settingsService.GetSettingsAsync(ct);
if (!settings.Federation.Enabled) return Results.Forbid();
// Формируем подписанный запрос к удаленному серверу
var targetUrl = $"{domain.TrimEnd('/')}/api/federation/v1/proxy/{id}";
var request = new HttpRequestMessage(HttpMethod.Get, targetUrl);
request.Headers.Add("X-Knot-Domain", settings.System.DomainUrl);
// Генерируем подпись запроса своим приватным ключом
using(var rsa = RSA.Create()) {
rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(settings.Federation.PrivateKey!), out _);
var dataToSign = Encoding.UTF8.GetBytes(id + settings.System.DomainUrl);
var signature = Convert.ToBase64String(rsa.SignData(dataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
request.Headers.Add("X-Knot-Signature", signature);
}
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
if (!response.IsSuccessStatusCode) return Results.NotFound();
var remoteStream = await response.Content.ReadAsStreamAsync(ct);
var contentType = response.Content.Headers.ContentType?.ToString() ?? "application/octet-stream";
return Results.Stream(remoteStream, contentType, enableRangeProcessing: true);
});
}
}