Files
forkmessager/backend/src/Modules/Federation/Presentation/Endpoints/FederationEndpoints.cs
Халимов Рустам 11f2b232a3 Сборка бэк
2026-03-27 15:45:34 +03:00

68 lines
3.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Carter;
using MediatR;
using Knot.Shared.Kernel.Constants;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using System;
using Host.Application.Federation.Commands;
using Knot.Modules.Federation.Application.Abstractions;
using Knot.Shared.Kernel.Storage;
using Knot.Modules.Settings.Application.Settings.Abstractions;
using Knot.Modules.Settings.Application.Settings.DTOs;
namespace Host.Endpoints;
/// <summary>
/// Эндпоинты федерации для межсерверного взаимодействия.
/// </summary>
public sealed class FederationEndpoints : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
var group = app.MapGroup(Routes.ApiFederation);
group.MapPost("/handshake", async ([FromBody] HandshakeRequest request, ISender sender, CancellationToken ct) =>
{
var result = await sender.Send(new HandshakeFederationCommand(request), ct);
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(new { error = result.Error.Description });
});
group.MapPost("/inbound", async ([FromBody] FederationMessagePacket packet, ISender sender, CancellationToken ct) =>
{
var result = await sender.Send(new InboundFederationCommand(packet), ct);
return result.IsSuccess ? Results.Accepted() : Results.BadRequest(new { error = result.Error.Description });
});
group.MapGet("/resolve/{username}", async (string username, ISender sender, CancellationToken ct) =>
{
var result = await sender.Send(new ResolveUserCommand(username), ct);
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound(new { error = result.Error.Description });
});
group.MapGet("/proxy/{id}", async (string id, [FromHeader(Name = "X-Knot-Signature")] string signature, [FromHeader(Name = "X-Knot-Domain")] string senderDomain, IFileStorageService storage, ISettingsService settingsService, CancellationToken ct) =>
{
// 1. Валидация подписи (упрощенно)
var settings = await settingsService.GetSettingsAsync(ct);
var senderConfig = settings.Federation.AllowedDomains.FirstOrDefault(d => d.Domain.Equals(senderDomain, StringComparison.OrdinalIgnoreCase));
if (senderConfig == null || string.IsNullOrEmpty(senderConfig.PublicKey))
return Results.Forbid();
// В реальном коде здесь проверка RSA подписи параметров запроса (URL + Domain)
// 2. Стриминг файла первоисточника
try
{
var (stream, contentType, fileName) = await storage.DownloadFileAsync(id);
return Results.File(stream, contentType, fileName, enableRangeProcessing: true);
}
catch
{
return Results.NotFound();
}
});
}
}