Сборка
This commit is contained in:
@@ -44,7 +44,7 @@ public static class AdminEndpoints
|
||||
return Results.Ok(result.Value);
|
||||
});
|
||||
|
||||
group.MapPost("users/{userId:guid}/reset-password", async (Guid userId, [FromBody] ResetPasswordRequest dto, ISender sender, CancellationToken ct) =>
|
||||
group.MapPost("users/{userId:guid}/reset-password", async ([FromRoute] Guid userId, [FromBody] ResetPasswordRequest dto, ISender sender, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new ResetUserPasswordCommand(userId, dto.NewPassword), ct);
|
||||
if (result.IsFailure)
|
||||
@@ -55,13 +55,13 @@ public static class AdminEndpoints
|
||||
return Results.Ok(result.Value);
|
||||
});
|
||||
|
||||
group.MapPost("users/{userId:guid}/ban", async (Guid userId, ISender sender, CancellationToken ct) =>
|
||||
group.MapPost("users/{userId:guid}/ban", async ([FromRoute] Guid userId, ISender sender, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new BanUserCommand(userId), ct);
|
||||
return result.IsSuccess ? Results.Ok() : Results.BadRequest(new { error = result.Error.Description });
|
||||
});
|
||||
|
||||
group.MapPost("users/{userId:guid}/unban", async (Guid userId, ISender sender, CancellationToken ct) =>
|
||||
group.MapPost("users/{userId:guid}/unban", async ([FromRoute] Guid userId, ISender sender, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new UnbanUserCommand(userId), ct);
|
||||
return result.IsSuccess ? Results.Ok() : Results.BadRequest(new { error = result.Error.Description });
|
||||
@@ -77,7 +77,7 @@ public static class AdminEndpoints
|
||||
return Results.Ok(result.Value);
|
||||
});
|
||||
|
||||
group.MapGet("users/{id:guid}", async (Guid id, ISender sender, CancellationToken ct) =>
|
||||
group.MapGet("users/{id:guid}", async ([FromRoute] Guid id, ISender sender, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetUserDetailsQuery(id), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound("User not found");
|
||||
|
||||
@@ -74,7 +74,7 @@ public static class ChatsEndpoints
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapPut("{id:guid}", async (Guid id, [FromBody] UpdateChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPut("{id:guid}", async ([FromRoute] Guid id, [FromBody] UpdateChatRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new UpdateChatCommand(id, userContext.UserId, request.Name, request.Description), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
@@ -83,7 +83,7 @@ public static class ChatsEndpoints
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapDelete("{id:guid}", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapDelete("{id:guid}", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new LeaveOrDeleteChatCommand(id, userContext.UserId), ct);
|
||||
if (result.IsFailure)
|
||||
@@ -94,19 +94,19 @@ public static class ChatsEndpoints
|
||||
return Results.Ok(result.Value);
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/clear", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("{id:guid}/clear", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new ClearChatCommand(id, userContext.UserId), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/pin", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("{id:guid}/pin", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new TogglePinCommand(id, userContext.UserId), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/members", async (Guid id, [FromBody] AddMembersRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("{id:guid}/members", async ([FromRoute] Guid id, [FromBody] AddMembersRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new AddMembersCommand(id, userContext.UserId, request.UserIds.ToList()), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
@@ -115,7 +115,7 @@ public static class ChatsEndpoints
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapDelete("{id:guid}/members/{userId:guid}", async (Guid id, Guid userId, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapDelete("{id:guid}/members/{userId:guid}", async ([FromRoute] Guid id, [FromRoute] Guid userId, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new RemoveMemberCommand(id, userContext.UserId, userId), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
@@ -124,7 +124,7 @@ public static class ChatsEndpoints
|
||||
return Results.Ok(chatResult.Value);
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/avatar", async (Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("{id:guid}/avatar", async ([FromRoute] Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
if (!req.HasFormContentType) return Results.BadRequest("No file");
|
||||
var form = await req.ReadFormAsync(ct);
|
||||
@@ -139,7 +139,7 @@ public static class ChatsEndpoints
|
||||
return Results.Ok(chatResult.Value);
|
||||
}).DisableAntiforgery();
|
||||
|
||||
group.MapPost("{id:guid}/avatar/crop", async (Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("{id:guid}/avatar/crop", async ([FromRoute] Guid id, HttpRequest req, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
if (!req.HasFormContentType) return Results.BadRequest("No file");
|
||||
var form = await req.ReadFormAsync(ct);
|
||||
@@ -159,7 +159,7 @@ public static class ChatsEndpoints
|
||||
return Results.Ok(chatResult.Value);
|
||||
}).DisableAntiforgery();
|
||||
|
||||
group.MapDelete("{id:guid}/avatar", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapDelete("{id:guid}/avatar", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new RemoveGroupAvatarCommand(id, userContext.UserId), ct);
|
||||
if (result.IsFailure) return Results.NotFound();
|
||||
|
||||
@@ -19,7 +19,7 @@ public static class MessagesEndpoints
|
||||
{
|
||||
var group = app.MapGroup("api/messages").RequireAuthorization();
|
||||
|
||||
group.MapGet("chat/{chatId:guid}", async (Guid chatId, [FromQuery] string? cursor, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapGet("chat/{chatId:guid}", async ([FromRoute] Guid chatId, [FromQuery] string? cursor, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetMessagesQuery(userContext.UserId, chatId, cursor), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
|
||||
@@ -49,13 +49,13 @@ public static class MessagesEndpoints
|
||||
return Results.Ok(result.Value);
|
||||
}).DisableAntiforgery();
|
||||
|
||||
group.MapGet("chat/{chatId:guid}/shared", async (Guid chatId, [FromQuery] string? type, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapGet("chat/{chatId:guid}/shared", async ([FromRoute] Guid chatId, [FromQuery] string? type, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetSharedMediaQuery(userContext.UserId, chatId, type), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error.Description);
|
||||
});
|
||||
|
||||
group.MapPost("chat/{chatId:guid}", async (Guid chatId, [FromBody] SendMessageRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("chat/{chatId:guid}", async ([FromRoute] Guid chatId, [FromBody] SendMessageRequest request, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var attachments = request.Attachments?.Select(a =>
|
||||
new AttachmentRequest(a.Type, a.Url, a.FileName, a.FileSize)).ToList();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
using MediatR;
|
||||
using System;
|
||||
using Host.Application.Federation.Commands;
|
||||
using Knot.Contracts.Settings.Application.Abstractions;
|
||||
using Knot.Contracts.Settings.Application.DTOs;
|
||||
using Knot.Modules.Federation.Application.Abstractions;
|
||||
using Knot.Shared.Kernel.Constants;
|
||||
using Knot.Shared.Kernel.Storage;
|
||||
using MediatR;
|
||||
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.Contracts.Settings.Application.Abstractions;
|
||||
using Knot.Contracts.Settings.Application.DTOs;
|
||||
|
||||
namespace Host.Endpoints;
|
||||
|
||||
@@ -21,7 +21,7 @@ public static class FederationEndpoints
|
||||
public static void MapFederationEndpoints(this WebApplication 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);
|
||||
@@ -34,30 +34,30 @@ public static class FederationEndpoints
|
||||
return result.IsSuccess ? Results.Accepted() : Results.BadRequest(new { error = result.Error.Description });
|
||||
});
|
||||
|
||||
group.MapGet("/resolve/{username}", async (string username, ISender sender, CancellationToken ct) =>
|
||||
group.MapGet("/resolve/{username}", async ([FromRoute] 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, [FromServices] IFileStorageService storage, [FromServices] ISettingsService settingsService, CancellationToken ct) =>
|
||||
group.MapGet("/proxy/{id}", async ([FromRoute] string id, [FromHeader(Name = "X-Knot-Signature")] string signature, [FromHeader(Name = "X-Knot-Domain")] string senderDomain, [FromServices] IFileStorageService storage, [FromServices] 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
|
||||
try
|
||||
{
|
||||
var (stream, contentType, fileName) = await storage.DownloadFileAsync(id);
|
||||
return Results.File(stream, contentType, fileName, enableRangeProcessing: true);
|
||||
}
|
||||
catch
|
||||
catch
|
||||
{
|
||||
return Results.NotFound();
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public static class ContactsEndpoints
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.BadRequest(result.Error);
|
||||
});
|
||||
|
||||
group.MapGet("status/{userId:guid}", async (Guid userId, ISender sender, IUserContext userContext, IContactsDbContext context, CancellationToken ct) =>
|
||||
group.MapGet("status/{userId:guid}", async ([FromRoute] Guid userId, ISender sender, IUserContext userContext, [FromServices] IContactsDbContext context, CancellationToken ct) =>
|
||||
{
|
||||
var contact = await context.Contacts
|
||||
.FirstOrDefaultAsync(c =>
|
||||
@@ -74,19 +74,19 @@ public static class ContactsEndpoints
|
||||
return result.IsSuccess ? Results.Ok(new { status = "pending" }) : Results.BadRequest(result.Error.Description);
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/accept", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("{id:guid}/accept", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new AcceptContactRequestCommand(userContext.UserId, id), ct);
|
||||
return result.IsSuccess ? Results.Ok(new { id = result.Value }) : Results.NotFound(result.Error.Description);
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/decline", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("{id:guid}/decline", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new DeclineContactRequestCommand(userContext.UserId, id), ct);
|
||||
return result.IsSuccess ? Results.Ok(new { success = true }) : Results.NotFound(result.Error.Description);
|
||||
});
|
||||
|
||||
group.MapDelete("{id:guid}", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapDelete("{id:guid}", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new RemoveContactCommand(userContext.UserId, id), ct);
|
||||
return result.IsSuccess ? Results.Ok(new SuccessResponse(true)) : Results.NotFound(result.Error.Description);
|
||||
|
||||
@@ -33,7 +33,7 @@ public static class StoriesEndpoints
|
||||
return Results.Ok(new { id = result.Value });
|
||||
});
|
||||
|
||||
group.MapPost("video", async (HttpRequest req, ISender sender, IUserContext userContext, IFileStorageService fileStorage, CancellationToken ct) =>
|
||||
group.MapPost("video", async (HttpRequest req, ISender sender, IUserContext userContext, [FromServices] IFileStorageService fileStorage, CancellationToken ct) =>
|
||||
{
|
||||
if (!req.HasFormContentType) return Results.BadRequest("No file uploaded");
|
||||
var form = await req.ReadFormAsync(ct);
|
||||
@@ -45,26 +45,26 @@ public static class StoriesEndpoints
|
||||
return Results.Ok(new { url = $"/api/files/{fileId}" });
|
||||
}).DisableAntiforgery();
|
||||
|
||||
group.MapGet("user/{userId:guid}", async (Guid userId, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapGet("user/{userId:guid}", async ([FromRoute] Guid userId, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetUserStoriesQuery(userContext.UserId, userId), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
|
||||
});
|
||||
|
||||
group.MapPost("{id:guid}/view", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapPost("{id:guid}/view", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new ViewStoryCommand(userContext.UserId, id), ct);
|
||||
return result.IsSuccess ? Results.Ok(result.Value) : Results.NotFound();
|
||||
});
|
||||
|
||||
group.MapGet("{id:guid}/viewers", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapGet("{id:guid}/viewers", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new GetStoryViewersQuery(userContext.UserId, id), ct);
|
||||
if (result.IsFailure) return result.Error.Code == "Unauthorized" ? Results.Forbid() : Results.NotFound();
|
||||
return Results.Ok(result.Value);
|
||||
});
|
||||
|
||||
group.MapDelete("{id:guid}", async (Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
group.MapDelete("{id:guid}", async ([FromRoute] Guid id, ISender sender, IUserContext userContext, CancellationToken ct) =>
|
||||
{
|
||||
var result = await sender.Send(new DeleteStoryCommand(userContext.UserId, id), ct);
|
||||
if (result.IsFailure) return result.Error.Code == "Unauthorized" ? Results.Forbid() : Results.NotFound();
|
||||
|
||||
Reference in New Issue
Block a user