Files
PnvPanel/backend/src/PnvPanel.Api/Endpoints/SubscriptionEndpoints.cs
T
Leonid Pershin 8b92204733
CI / Backend (build + test) (push) Successful in 1m15s
CI / Frontend (lint + typecheck + build) (push) Successful in 30s
Enhance API endpoints with response type annotations
- Updated various API endpoints to include response type annotations using .Produces<T>() for better documentation and type safety.
- Enhanced activation, admin, user, config, and other endpoints to specify response types, improving clarity for frontend integration.
- Added new DTOs for structured responses in authentication and Telegram-related endpoints.
- Improved overall API schema generation to reflect these changes, ensuring consistency between backend and frontend types.
2026-07-02 12:56:03 +03:00

45 lines
2.1 KiB
C#

using System.Text;
using PnvPanel.Api.Common;
using PnvPanel.Application.Common.Messaging;
using PnvPanel.Application.Subscriptions;
namespace PnvPanel.Api.Endpoints;
public static class SubscriptionEndpoints
{
public static IEndpointRouteBuilder MapSubscriptionEndpoints(this IEndpointRouteBuilder app)
{
// Вне /api по дизайну (api-design.md) — публичный эндпоинт для VPN-клиентов.
app.MapGet("/sub/{token}", GetSubscription)
.WithTags("Subscription")
.RequireRateLimiting(RateLimiting.AuthPolicy)
.Produces<string>(StatusCodes.Status200OK, "text/plain")
.Produces(StatusCodes.Status404NotFound);
return app;
}
private static async Task<IResult> GetSubscription(string token, HttpResponse response, ISender sender, CancellationToken cancellationToken)
{
// Токен — либо AppUser.SubscriptionToken (агрегированная подписка), либо VpnConfig.SubscriptionToken
// (один конфиг). Пробуем пользовательский токен первым.
var userResult = await sender.Send(new GetUserSubscriptionQuery(token), cancellationToken);
var result = userResult.IsSuccess ? userResult : await sender.Send(new GetConfigSubscriptionQuery(token), cancellationToken);
if (!result.IsSuccess)
return Results.NotFound();
var body = string.Join('\n', result.Value.ConnectionStrings);
var base64Body = Convert.ToBase64String(Encoding.UTF8.GetBytes(body));
var total = result.Value.UsedUpBytes + result.Value.UsedDownBytes;
var expire = result.Value.ExpiresAt is { } exp ? exp.ToUnixTimeSeconds().ToString() : "0";
response.Headers.Append(
"Subscription-Userinfo",
$"upload={result.Value.UsedUpBytes}; download={result.Value.UsedDownBytes}; total={total}; expire={expire}");
response.Headers.Append("Profile-Update-Interval", "12");
return Results.Text(base64Body, "text/plain; charset=utf-8");
}
}