feat: implement Blazor web interface for GM session management
Deploy Telegram Bot / deploy (push) Has been cancelled

- Created GmRelay.Web project (Blazor Server)
- Created GmRelay.Shared library for domain models and rendering
- Refactored GmRelay.Bot to use the Shared library
- Integrated Telegram Login widget with server-side HMAC verification
- Added Dashboard, Group Details, and Edit Session pages
- Enabled bot notifications and in-place message updates from web actions
- Updated .NET Aspire orchestration and Docker Compose configuration
This commit is contained in:
2026-04-17 11:06:59 +03:00
parent c27456e726
commit 988133e389
93 changed files with 61016 additions and 34 deletions
@@ -0,0 +1,59 @@
using System.Security.Cryptography;
using System.Text;
namespace GmRelay.Web.Services;
public sealed class TelegramAuthService(IConfiguration configuration)
{
public bool Verify(IQueryCollection query, out long telegramId, out string name)
{
telegramId = 0;
name = string.Empty;
if (!query.TryGetValue("hash", out var hash))
return false;
var token = configuration["Telegram__BotToken"] ?? configuration["Telegram:BotToken"];
if (string.IsNullOrEmpty(token))
return false;
// 1. Sort and join
var dataCheckList = query
.Where(x => x.Key != "hash")
.OrderBy(x => x.Key)
.Select(x => $"{x.Key}={x.Value}")
.ToList();
var dataCheckString = string.Join("\n", dataCheckList);
// 2. Compute Secret Key
using var sha256 = SHA256.Create();
var secretKey = sha256.ComputeHash(Encoding.UTF8.GetBytes(token));
// 3. Compute Hash
using var hmac = new HMACSHA256(secretKey);
var computedHashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(dataCheckString));
var computedHash = Convert.ToHexString(computedHashBytes).ToLower();
if (computedHash != hash.ToString().ToLower())
return false;
// 4. Check expiration (auth_date)
if (query.TryGetValue("auth_date", out var authDateStr) && long.TryParse(authDateStr, out var authDate))
{
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
if (now - authDate > 86400) // 24 hours
return false;
}
if (query.TryGetValue("id", out var idStr) && long.TryParse(idStr, out telegramId))
{
var firstName = query["first_name"].ToString();
var lastName = query["last_name"].ToString();
name = string.IsNullOrWhiteSpace(lastName) ? firstName : $"{firstName} {lastName}";
return true;
}
return false;
}
}