Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8220f2060f |
@@ -6,7 +6,7 @@ on:
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VERSION: 1.9.0
|
VERSION: 1.9.1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# ЧАСТЬ 1: Собираем образы и кладем в Gitea (чтобы делиться с ребятами)
|
# ЧАСТЬ 1: Собираем образы и кладем в Gitea (чтобы делиться с ребятами)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>1.9.0</Version>
|
<Version>1.9.1</Version>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Проект разработан с упором на производительность, архитектуру Vertical Slice, Native AOT (для бота) и удобство развертывания с использованием .NET Aspire.
|
Проект разработан с упором на производительность, архитектуру Vertical Slice, Native AOT (для бота) и удобство развертывания с использованием .NET Aspire.
|
||||||
|
|
||||||
**Текущая версия:** `v1.9.0`.
|
**Текущая версия:** `v1.9.1`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
### 🌐 Web Dashboard (Blazor Server)
|
### 🌐 Web Dashboard (Blazor Server)
|
||||||
- **🔐 Авторизация через Telegram**: Безопасный вход с использованием Telegram Login Widget (HMAC-SHA256 валидация).
|
- **🔐 Авторизация через 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.
|
- **🤝 Co-GM и делегирование**: Owner группы назначает помощников по Telegram ID, а co-GM получает доступ к управлению расписанием в Telegram и Web Dashboard.
|
||||||
- **📋 Шаблоны кампаний**: Owner и co-GM управляют типовыми параметрами кампаний в отдельной вкладке `Шаблоны`, а на странице группы запускают новый повторяющийся batch из выбранного шаблона.
|
- **📋 Шаблоны кампаний**: Owner и co-GM управляют типовыми параметрами кампаний в отдельной вкладке `Шаблоны`, а на странице группы запускают новый повторяющийся batch из выбранного шаблона.
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
|
|
||||||
bot:
|
bot:
|
||||||
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.9.0
|
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.9.1
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
@@ -30,7 +30,7 @@ services:
|
|||||||
- gmrelay
|
- gmrelay
|
||||||
|
|
||||||
web:
|
web:
|
||||||
image: git.codeanddice.ru/toutsu/gmrelay-web:1.9.0
|
image: git.codeanddice.ru/toutsu/gmrelay-web:1.9.1
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
|
|||||||
@@ -24,12 +24,30 @@
|
|||||||
<ReconnectModal />
|
<ReconnectModal />
|
||||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
||||||
<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) {
|
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');
|
document.body.classList.add('telegram-mini-app');
|
||||||
webApp.ready();
|
webApp.ready();
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
window.loadTelegramWidget = function (botUsername, authUrl) {
|
window.loadTelegramWidget = function (botUsername, authUrl) {
|
||||||
@@ -46,12 +64,58 @@
|
|||||||
container.appendChild(script);
|
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) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var webApp = window.Telegram.WebApp;
|
|
||||||
document.body.classList.add('telegram-mini-app');
|
document.body.classList.add('telegram-mini-app');
|
||||||
webApp.ready();
|
webApp.ready();
|
||||||
webApp.expand();
|
webApp.expand();
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="nav-version">v1.9.0</div>
|
<div class="nav-version">v1.9.1</div>
|
||||||
</div>
|
</div>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
statusMessage = "Mini App доступен из Telegram. Для браузера используйте обычный вход.";
|
statusMessage = "Mini App доступен из Telegram. Для браузера используйте обычный вход.";
|
||||||
showFallback = true;
|
showFallback = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
await TryWatchLoginAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (JSException)
|
catch (JSException)
|
||||||
@@ -65,6 +66,18 @@
|
|||||||
statusMessage = "Не удалось получить данные Telegram Mini App. Попробуйте открыть dashboard из бота.";
|
statusMessage = "Не удалось получить данные Telegram Mini App. Попробуйте открыть dashboard из бота.";
|
||||||
showFallback = true;
|
showFallback = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
await TryWatchLoginAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TryWatchLoginAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await JS.InvokeVoidAsync("watchTelegramMiniAppLogin", "/auth/status", "/");
|
||||||
|
}
|
||||||
|
catch (JSException)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,9 @@ app.MapPost("/auth/telegram-webapp", async (
|
|||||||
return Results.Ok(new { redirectUrl = "/" });
|
return Results.Ok(new { redirectUrl = "/" });
|
||||||
}).DisableAntiforgery();
|
}).DisableAntiforgery();
|
||||||
|
|
||||||
|
app.MapGet("/auth/status", (HttpContext context) =>
|
||||||
|
Results.Ok(new { authenticated = context.User.Identity?.IsAuthenticated == true }));
|
||||||
|
|
||||||
app.MapPost("/auth/logout", async (HttpContext context) =>
|
app.MapPost("/auth/logout", async (HttpContext context) =>
|
||||||
{
|
{
|
||||||
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* ============================================
|
/* ============================================
|
||||||
GM-Relay Design System v1.9.0
|
GM-Relay Design System v1.9.1
|
||||||
Dark RPG Dashboard Theme
|
Dark RPG Dashboard Theme
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ public sealed class MiniAppDashboardTests
|
|||||||
Assert.Contains("@page \"/miniapp\"", miniAppPage, StringComparison.Ordinal);
|
Assert.Contains("@page \"/miniapp\"", miniAppPage, StringComparison.Ordinal);
|
||||||
Assert.Contains("authenticateTelegramMiniApp", miniAppPage, StringComparison.Ordinal);
|
Assert.Contains("authenticateTelegramMiniApp", miniAppPage, StringComparison.Ordinal);
|
||||||
Assert.Contains("/auth/telegram-webapp", 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]
|
[Fact]
|
||||||
@@ -20,6 +22,9 @@ public sealed class MiniAppDashboardTests
|
|||||||
Assert.Contains("telegram-web-app.js", appShell, StringComparison.Ordinal);
|
Assert.Contains("telegram-web-app.js", appShell, StringComparison.Ordinal);
|
||||||
Assert.Contains("window.authenticateTelegramMiniApp", appShell, StringComparison.Ordinal);
|
Assert.Contains("window.authenticateTelegramMiniApp", appShell, StringComparison.Ordinal);
|
||||||
Assert.Contains("Telegram.WebApp.initData", 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]
|
[Fact]
|
||||||
@@ -29,6 +34,8 @@ public sealed class MiniAppDashboardTests
|
|||||||
|
|
||||||
Assert.Contains("MapPost(\"/auth/telegram-webapp\"", program, StringComparison.Ordinal);
|
Assert.Contains("MapPost(\"/auth/telegram-webapp\"", program, StringComparison.Ordinal);
|
||||||
Assert.Contains("VerifyWebAppInitData", program, StringComparison.Ordinal);
|
Assert.Contains("VerifyWebAppInitData", program, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("MapGet(\"/auth/status\"", program, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("authenticated", program, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
Reference in New Issue
Block a user