fix: refresh mini app login state
Deploy Telegram Bot / build-and-push (push) Successful in 4m23s
Deploy Telegram Bot / deploy (push) Successful in 12s

This commit is contained in:
2026-04-28 17:03:53 +03:00
parent 41f2ea6e90
commit 8220f2060f
10 changed files with 102 additions and 15 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ on:
- main
env:
VERSION: 1.9.0
VERSION: 1.9.1
jobs:
# ЧАСТЬ 1: Собираем образы и кладем в Gitea (чтобы делиться с ребятами)
+1 -1
View File
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>1.9.0</Version>
<Version>1.9.1</Version>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
+2 -2
View File
@@ -4,7 +4,7 @@
Проект разработан с упором на производительность, архитектуру Vertical Slice, Native AOT (для бота) и удобство развертывания с использованием .NET Aspire.
**Текущая версия:** `v1.9.0`.
**Текущая версия:** `v1.9.1`.
---
@@ -24,7 +24,7 @@
### 🌐 Web Dashboard (Blazor Server)
- **🔐 Авторизация через Telegram**: Безопасный вход с использованием Telegram Login Widget (HMAC-SHA256 валидация).
- **📱 Telegram Mini App Dashboard**: Мобильная версия dashboard открывается прямо из Telegram, проверяет WebApp `initData` на сервере и использует те же права owner/co-GM, что и обычный Web Dashboard.
- **📱 Telegram Mini App Dashboard**: Мобильная версия dashboard открывается прямо из Telegram, проверяет WebApp `initData` на сервере и использует те же права owner/co-GM, что и обычный Web Dashboard. Mini App ждёт данные Telegram при старте и автоматически обновляет состояние входа после внешнего Telegram Login.
- **📝 Удобное редактирование**: Веб-интерфейс для детального редактирования сессий, изменения дат, названий и статусов.
- **🤝 Co-GM и делегирование**: Owner группы назначает помощников по Telegram ID, а co-GM получает доступ к управлению расписанием в Telegram и Web Dashboard.
- **📋 Шаблоны кампаний**: Owner и co-GM управляют типовыми параметрами кампаний в отдельной вкладке `Шаблоны`, а на странице группы запускают новый повторяющийся batch из выбранного шаблона.
+2 -2
View File
@@ -17,7 +17,7 @@ services:
retries: 10
bot:
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.9.0
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.9.1
restart: always
depends_on:
db:
@@ -30,7 +30,7 @@ services:
- gmrelay
web:
image: git.codeanddice.ru/toutsu/gmrelay-web:1.9.0
image: git.codeanddice.ru/toutsu/gmrelay-web:1.9.1
restart: always
depends_on:
db:
+69 -5
View File
@@ -24,12 +24,30 @@
<ReconnectModal />
<script src="@Assets["_framework/blazor.web.js"]"></script>
<script>
(function () {
window.waitForTelegramMiniAppInitData = async function (timeoutMs) {
var deadline = Date.now() + (timeoutMs || 3000);
while (Date.now() <= deadline) {
if (window.Telegram && window.Telegram.WebApp && window.Telegram.WebApp.initData) {
var webApp = window.Telegram.WebApp;
return window.Telegram.WebApp;
}
await new Promise(function (resolve) {
setTimeout(resolve, 100);
});
}
return null;
};
(async function () {
var webApp = await window.waitForTelegramMiniAppInitData(1000);
if (!webApp) {
return;
}
document.body.classList.add('telegram-mini-app');
webApp.ready();
}
})();
window.loadTelegramWidget = function (botUsername, authUrl) {
@@ -46,12 +64,58 @@
container.appendChild(script);
};
window.watchTelegramMiniAppLogin = function (statusUrl, redirectUrl) {
if (window.gmRelayMiniAppLoginWatcher) {
return;
}
var stopWatching = function () {
if (window.gmRelayMiniAppLoginWatcher) {
window.clearInterval(window.gmRelayMiniAppLoginWatcher);
window.gmRelayMiniAppLoginWatcher = null;
}
};
var checkLogin = async function () {
try {
var response = await fetch(statusUrl, {
credentials: 'same-origin',
cache: 'no-store'
});
if (!response.ok) {
return;
}
var payload = await response.json();
if (payload.authenticated) {
stopWatching();
window.location.href = redirectUrl || '/';
}
} catch (error) {
return;
}
};
window.gmRelayMiniAppLoginWatcher = window.setInterval(checkLogin, 1000);
window.addEventListener('focus', function () {
void checkLogin();
});
document.addEventListener('visibilitychange', function () {
if (!document.hidden) {
void checkLogin();
}
});
void checkLogin();
};
window.authenticateTelegramMiniApp = async function (authUrl, redirectUrl) {
if (!window.Telegram || !window.Telegram.WebApp || !window.Telegram.WebApp.initData) {
var webApp = await window.waitForTelegramMiniAppInitData(3000);
if (!webApp) {
return false;
}
var webApp = window.Telegram.WebApp;
document.body.classList.add('telegram-mini-app');
webApp.ready();
webApp.expand();
@@ -56,7 +56,7 @@
</button>
</form>
<div class="nav-version">v1.9.0</div>
<div class="nav-version">v1.9.1</div>
</div>
</Authorized>
<NotAuthorized>
@@ -58,6 +58,7 @@
statusMessage = "Mini App доступен из Telegram. Для браузера используйте обычный вход.";
showFallback = true;
StateHasChanged();
await TryWatchLoginAsync();
}
}
catch (JSException)
@@ -65,6 +66,18 @@
statusMessage = "Не удалось получить данные Telegram Mini App. Попробуйте открыть dashboard из бота.";
showFallback = true;
StateHasChanged();
await TryWatchLoginAsync();
}
}
private async Task TryWatchLoginAsync()
{
try
{
await JS.InvokeVoidAsync("watchTelegramMiniAppLogin", "/auth/status", "/");
}
catch (JSException)
{
}
}
}
+3
View File
@@ -117,6 +117,9 @@ app.MapPost("/auth/telegram-webapp", async (
return Results.Ok(new { redirectUrl = "/" });
}).DisableAntiforgery();
app.MapGet("/auth/status", (HttpContext context) =>
Results.Ok(new { authenticated = context.User.Identity?.IsAuthenticated == true }));
app.MapPost("/auth/logout", async (HttpContext context) =>
{
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+1 -1
View File
@@ -1,5 +1,5 @@
/* ============================================
GM-Relay Design System v1.9.0
GM-Relay Design System v1.9.1
Dark RPG Dashboard Theme
============================================ */
@@ -10,6 +10,8 @@ public sealed class MiniAppDashboardTests
Assert.Contains("@page \"/miniapp\"", miniAppPage, StringComparison.Ordinal);
Assert.Contains("authenticateTelegramMiniApp", miniAppPage, StringComparison.Ordinal);
Assert.Contains("/auth/telegram-webapp", miniAppPage, StringComparison.Ordinal);
Assert.Contains("watchTelegramMiniAppLogin", miniAppPage, StringComparison.Ordinal);
Assert.Contains("/auth/status", miniAppPage, StringComparison.Ordinal);
}
[Fact]
@@ -20,6 +22,9 @@ public sealed class MiniAppDashboardTests
Assert.Contains("telegram-web-app.js", appShell, StringComparison.Ordinal);
Assert.Contains("window.authenticateTelegramMiniApp", appShell, StringComparison.Ordinal);
Assert.Contains("Telegram.WebApp.initData", appShell, StringComparison.Ordinal);
Assert.Contains("window.waitForTelegramMiniAppInitData", appShell, StringComparison.Ordinal);
Assert.Contains("window.watchTelegramMiniAppLogin", appShell, StringComparison.Ordinal);
Assert.Contains("setTimeout(resolve, 100)", appShell, StringComparison.Ordinal);
}
[Fact]
@@ -29,6 +34,8 @@ public sealed class MiniAppDashboardTests
Assert.Contains("MapPost(\"/auth/telegram-webapp\"", program, StringComparison.Ordinal);
Assert.Contains("VerifyWebAppInitData", program, StringComparison.Ordinal);
Assert.Contains("MapGet(\"/auth/status\"", program, StringComparison.Ordinal);
Assert.Contains("authenticated", program, StringComparison.Ordinal);
}
[Fact]