Enhance user plan management and update related endpoints
CI / Backend (build + test) (push) Failing after 1m23s
CI / Frontend (lint + typecheck + build) (push) Successful in 34s

- Added new configuration options for user plans in `.env.example`, including `Plans__MaxCustomConfigCount` and `Plans__MinCustomConfigCount`.
- Introduced `MapPlanEndpoints` in `Program.cs` to handle plan-related API routes.
- Implemented `SetUserPlan` endpoint in `RoleEndpoints` to allow admins to assign plans to users.
- Removed deprecated role request approval endpoints from `AdminSupportEndpoints`.
- Updated `ITelegramNotifier` and related classes to reflect changes in role request handling and payment notifications.
- Refactored role management commands to remove `MaxConfigs` and focus on `MaxIpLimit` and billing settings.
- Enhanced billing request handling to accommodate plan changes instead of role changes.
- Updated various interfaces and command handlers to support new plan management features.
This commit is contained in:
Leonid Pershin
2026-07-23 22:52:20 +03:00
parent 2c5b730500
commit fad03c2834
152 changed files with 4060 additions and 2240 deletions
@@ -104,47 +104,6 @@ internal sealed class TelegramNotifier(
}
}
public async Task NotifyAdminsRoleRequestCreatedAsync(
Guid ticketId,
string userName,
string roleDescription,
string justification,
CancellationToken cancellationToken
)
{
if (string.IsNullOrWhiteSpace(options.Value.BotToken))
return;
var text =
$"🆕 Заявка на роль от <b>{Escape(userName)}</b>\n{Escape(roleDescription)}\nОбоснование: {Escape(justification)}";
var keyboard = new InlineKeyboardMarkup(
new[]
{
InlineKeyboardButton.WithCallbackData("✅ Одобрить", $"rrq:approve:{ticketId}"),
InlineKeyboardButton.WithCallbackData("❌ Отклонить", $"rrq:reject:{ticketId}"),
}
);
foreach (var adminId in options.Value.ParseAdminTelegramUserIds())
{
try
{
await botClient.SendMessage(
adminId,
text,
parseMode: ParseMode.Html,
replyMarkup: keyboard,
cancellationToken: cancellationToken
);
}
catch
{
// Админ мог не запускать бота (нет чата с ботом) — пропускаем, не валим команду.
}
}
}
public async Task NotifyAdminsExtensionRequestCreatedAsync(
Guid ticketId,
string userName,
@@ -196,8 +155,7 @@ internal sealed class TelegramNotifier(
if (string.IsNullOrWhiteSpace(options.Value.BotToken))
return;
var typeLabel = type == TicketType.RoleRequest ? "заявка на роль" : "баг/предложение";
var text = $"🔓 Тикет от <b>{Escape(userName)}</b> переоткрыт ({typeLabel})";
var text = $"🔓 Тикет от <b>{Escape(userName)}</b> переоткрыт (баг/предложение)";
InlineKeyboardMarkup? keyboard = null;
if (!string.IsNullOrWhiteSpace(options.Value.PublicSiteUrl))
@@ -280,7 +238,7 @@ internal sealed class TelegramNotifier(
if (string.IsNullOrWhiteSpace(options.Value.BotToken))
return;
var reason = kind == PaymentRequestKind.RoleChangeTopUp ? "доплате за смену роли" : $"оплате за {PeriodLabel(period!.Value)}";
var reason = kind == PaymentRequestKind.PlanChangeTopUp ? "доплате за смену тарифа" : $"оплате за {PeriodLabel(period!.Value)}";
var text = $"💰 <b>{Escape(userName)}</b> заявляет о {reason} — {amount} ₽\nПроверьте поступление и подтвердите.";
var keyboard = new InlineKeyboardMarkup(