fix: refresh login fallback in mini app

This commit is contained in:
2026-04-28 17:20:29 +03:00
parent 8220f2060f
commit b78c24900b
9 changed files with 79 additions and 14 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ on:
- main
env:
VERSION: 1.9.1
VERSION: 1.9.2
jobs:
# ЧАСТЬ 1: Собираем образы и кладем в Gitea (чтобы делиться с ребятами)
+1 -1
View File
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>1.9.1</Version>
<Version>1.9.2</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.1`.
**Текущая версия:** `v1.9.2`.
---
@@ -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. Mini App ждёт данные Telegram при старте и автоматически обновляет состояние входа после внешнего Telegram Login.
- **📱 Telegram Mini App Dashboard**: Мобильная версия dashboard открывается прямо из Telegram, проверяет WebApp `initData` на сервере и использует те же права owner/co-GM, что и обычный Web Dashboard. Mini App ждёт данные Telegram при старте и автоматически обновляет состояние входа после внешнего Telegram Login, включая возврат на страницу `/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.1
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.9.2
restart: always
depends_on:
db:
@@ -30,7 +30,7 @@ services:
- gmrelay
web:
image: git.codeanddice.ru/toutsu/gmrelay-web:1.9.1
image: git.codeanddice.ru/toutsu/gmrelay-web:1.9.2
restart: always
depends_on:
db:
+56 -6
View File
@@ -64,11 +64,16 @@
container.appendChild(script);
};
window.watchTelegramMiniAppLogin = function (statusUrl, redirectUrl) {
window.watchTelegramMiniAppLogin = function (statusUrl, redirectUrl, reloadOnReturn) {
window.gmRelayMiniAppLoginReloadOnReturn =
window.gmRelayMiniAppLoginReloadOnReturn || reloadOnReturn === true;
if (window.gmRelayMiniAppLoginWatcher) {
return;
}
window.gmRelayMiniAppLoginLeftPage = false;
var stopWatching = function () {
if (window.gmRelayMiniAppLoginWatcher) {
window.clearInterval(window.gmRelayMiniAppLoginWatcher);
@@ -76,7 +81,41 @@
}
};
var checkLogin = async function () {
var reloadAfterExternalLogin = function () {
if (!window.gmRelayMiniAppLoginReloadOnReturn || !window.gmRelayMiniAppLoginLeftPage) {
return;
}
window.gmRelayMiniAppLoginLeftPage = false;
try {
var refreshKey = 'gmrelay-miniapp-login-refresh:' + window.location.pathname;
if (window.sessionStorage && window.sessionStorage.getItem(refreshKey) === '1') {
return;
}
if (window.sessionStorage) {
window.sessionStorage.setItem(refreshKey, '1');
}
} catch (error) {
}
window.location.reload();
};
var allowNextExternalLoginReload = function () {
window.gmRelayMiniAppLoginLeftPage = true;
try {
var refreshKey = 'gmrelay-miniapp-login-refresh:' + window.location.pathname;
if (window.sessionStorage) {
window.sessionStorage.removeItem(refreshKey);
}
} catch (error) {
}
};
var checkLogin = async function (reloadWhenUnauthenticated) {
try {
var response = await fetch(statusUrl, {
credentials: 'same-origin',
@@ -91,6 +130,11 @@
if (payload.authenticated) {
stopWatching();
window.location.href = redirectUrl || '/';
return;
}
if (reloadWhenUnauthenticated) {
reloadAfterExternalLogin();
}
} catch (error) {
return;
@@ -98,16 +142,22 @@
};
window.gmRelayMiniAppLoginWatcher = window.setInterval(checkLogin, 1000);
window.addEventListener('blur', function () {
allowNextExternalLoginReload();
});
window.addEventListener('focus', function () {
void checkLogin();
void checkLogin(true);
});
document.addEventListener('visibilitychange', function () {
if (!document.hidden) {
void checkLogin();
if (document.hidden) {
allowNextExternalLoginReload();
return;
}
void checkLogin(true);
});
void checkLogin();
void checkLogin(false);
};
window.authenticateTelegramMiniApp = async function (authUrl, redirectUrl) {
@@ -56,7 +56,7 @@
</button>
</form>
<div class="nav-version">v1.9.1</div>
<div class="nav-version">v1.9.2</div>
</div>
</Authorized>
<NotAuthorized>
@@ -47,6 +47,7 @@
if (firstRender)
{
await JS.InvokeVoidAsync("loadTelegramWidget", BotUsername, AuthUrl);
await JS.InvokeVoidAsync("watchTelegramMiniAppLogin", "/auth/status", "/", true);
}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
/* ============================================
GM-Relay Design System v1.9.1
GM-Relay Design System v1.9.2
Dark RPG Dashboard Theme
============================================ */
@@ -25,6 +25,9 @@ public sealed class MiniAppDashboardTests
Assert.Contains("window.waitForTelegramMiniAppInitData", appShell, StringComparison.Ordinal);
Assert.Contains("window.watchTelegramMiniAppLogin", appShell, StringComparison.Ordinal);
Assert.Contains("setTimeout(resolve, 100)", appShell, StringComparison.Ordinal);
Assert.Contains("reloadOnReturn", appShell, StringComparison.Ordinal);
Assert.Contains("gmRelayMiniAppLoginLeftPage", appShell, StringComparison.Ordinal);
Assert.Contains("window.location.reload()", appShell, StringComparison.Ordinal);
}
[Fact]
@@ -48,6 +51,17 @@ public sealed class MiniAppDashboardTests
Assert.Contains("@media (max-width: 768px)", css, StringComparison.Ordinal);
}
[Fact]
public async Task LoginPage_ShouldRefreshMiniAppAfterExternalTelegramLogin()
{
var loginPage = await File.ReadAllTextAsync(FindRepositoryFile("src/GmRelay.Web/Components/Pages/Login.razor"));
Assert.Contains(
"JS.InvokeVoidAsync(\"watchTelegramMiniAppLogin\", \"/auth/status\", \"/\", true)",
loginPage,
StringComparison.Ordinal);
}
private static string FindRepositoryFile(string relativePath)
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);