3 Commits
Author SHA1 Message Date
maeneko 33dd488971 Release 0.5.1
ci / check (push) In progress
release / release (push) Waiting to run
2026-10-29 23:52:26 +03:00
maeneko 27f4a9191a Send the token with /ui/brand from the settings tab
The route has been behind auth since aa848de, but the settings tab still
called it without a token: the 401 "Unauthorized" made the panel treat
the session as expired and log out on every visit to Settings. The mock
now requires auth on /ui/brand too, like the server.
2026-10-29 23:51:21 +03:00
maeneko 1ea2eda04a Keep the session on network errors when restoring it
Only a 401 clears the saved token; a network error or 5xx while the
panel restarts retries every 5 s instead of dropping to the login screen.
2026-10-29 23:49:49 +03:00
10 changed files with 30 additions and 16 deletions
+1 -1
View File
@@ -11,4 +11,4 @@
BRAND=Forgetting
CHANNEL=Beta
VERSION=0.5.0
VERSION=0.5.1
+1 -1
View File
@@ -377,7 +377,7 @@ TOKEN=$(curl -s -X POST http://HOST:PORT/login \
| Метод | Ответ |
|---|---|
| `GET /ui/brand` | `{ "brand": "Forgetting", "channel": "Beta", "version": "0.5.0" }` — **без авторизации**, панель берёт отсюда вордмарк на экране логина |
| `GET /ui/brand` | `{ "brand": "Forgetting", "channel": "Beta", "version": "0.5.1" }` — имя и версия панели для вордмарка; за авторизацией, чтобы сканер не опознал панель |
| `GET /health` | `{ "status", "server", "ip", "gen", "awg": { "status", "peers", "module", "tools" } }`. `200`, если интерфейс поднят, иначе `503` и `status: "degraded"` |
| `GET /awg/status` | `{ "up": true, "peers": 3, "publicKey": "…" }` |
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "awg-agent",
"version": "0.5.0",
"version": "0.5.1",
"private": true,
"description": "Агент ноды: держит исходящее WSS-соединение к core и исполняет запросы на локальном awg-ctrl",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "awg-control",
"version": "0.5.0",
"version": "0.5.1",
"description": "",
"main": "dist/index.js",
"scripts": {
+4 -2
View File
@@ -15,7 +15,7 @@ const now = () => Math.floor(Date.now() / 1000);
const rnd = (n: number) => Math.floor(Math.random() * n);
const b64 = (n: number) => crypto.randomBytes(n).toString("base64");
const GEN = "3.1";
const MOCK_VERSION = "0.5.0";
const MOCK_VERSION = "0.5.1";
// ── пользователи (vpn://) ──
interface U { name: string; ip: string; pub_key: string; vpn_key: string; key_gen: string; online: boolean; lastHandshake: number; rx: number; tx: number }
@@ -67,7 +67,9 @@ const devOut = (m: MK, d: Dev) => ({ ...d,
ips: m.servers.filter(s => s !== 2).map(s => ({ server_id: s, ip: `10.9.0.${50 + d.id + s * 10}` })) });
const find = (id: string) => masters.find(m => m.id === Number(id) && !m.deleting);
app.get("/ui/brand", (_q, r) => r.json({ brand: "Forgetting", channel: "Beta", version: MOCK_VERSION }));
// Как на сервере: за авторизацией, без токена — 401 «Unauthorized» (на него панель выходит из сессии).
app.get("/ui/brand", (q, r) => { if (!q.headers.authorization) { r.status(401).json({ error: "Unauthorized" }); return; }
r.json({ brand: "Forgetting", channel: "Beta", version: MOCK_VERSION }); });
app.post("/login", (q, r) => q.body.user === "admin" && q.body.pass === "admin" ? r.json({ token: "mock." + crypto.randomBytes(8).toString("hex") + ".sig" }) : r.status(401).json({ error: "Неверный логин или пароль" }));
app.post("/logout", (_q, r) => r.json({ ok: true }));
app.get("/health", (q, r) => {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "awg-ui",
"version": "0.5.0",
"version": "0.5.1",
"private": true,
"scripts": {
"dev": "vite",
+15 -3
View File
@@ -174,13 +174,25 @@ export default function App() {
// Восстановить сессию из сохранённого токена
useEffect(() => {
// Сбрасываем сессию только на 401: сетевой сбой или 502, пока панель перезапускается,
// раньше тоже выкидывали на экран входа. В остальных случаях пробуем снова через 5 с.
const saved = localStorage.getItem(TOKEN_KEY);
if (saved) {
startSession(saved).catch(() => {
if (!saved) return;
let timer: ReturnType<typeof setTimeout> | undefined;
let alive = true;
const attempt = () => {
startSession(saved).catch(e => {
if (!alive) return;
if (axios.isAxiosError(e) && e.response?.status === 401) {
localStorage.removeItem(TOKEN_KEY);
setToken('');
});
} else {
timer = setTimeout(attempt, 5000);
}
});
};
attempt();
return () => { alive = false; clearTimeout(timer); };
}, [startSession]);
// Состояние серверов (онлайн, пиры) обновляем раз в 15 с, пока открыта панель.
+2 -2
View File
@@ -2,7 +2,6 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
import { useState, useEffect, useLayoutEffect, useCallback, useRef } from 'react';
import axios from 'axios';
import { apiFetch, copyText, timeAgo, BRAND_FALLBACK, SETTINGS_SECTION_KEY, type Brand, type NodeInfo, type PageProps } from '../../lib/shared';
import { useTheme, setThemeMode, type ThemeMode } from '../../lib/theme';
import { IcoCheck, IcoCopy, IcoRefresh, IcoTrash, IcoPlus } from '../../components/icons';
@@ -138,7 +137,8 @@ export default function SettingsPage({ token, showMsg }: PageProps) {
} catch {
showMsg('Ошибка загрузки настроек');
}
axios.get('/ui/brand').then(({ data }) => setBrand({
// /ui/brand за авторизацией: без токена 401 «Unauthorized», и App считал сессию истёкшей.
apiFetch('GET', '/ui/brand', token).then(data => setBrand({
brand: data.brand || BRAND_FALLBACK, channel: data.channel || '', version: data.version || '',
})).catch(() => {});
apiFetch('GET', '/ui/nodes', token).then(r => setNodes(r.nodes ?? [])).catch(() => {});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "awg-cli",
"version": "0.5.0",
"version": "0.5.1",
"private": true,
"dependencies": {
"tsx": "^4.7.0",
+1 -1
View File
@@ -188,7 +188,7 @@ trap 'exec >&- 2>&-; wait "$LOG_PID" 2>/dev/null || true; exec 3>&-
# ⚠️ Дублирует .env из репозитория: установщик собирает URL релиза и печатает
# баннер ДО того, как архив с .env скачан, поэтому взять их оттуда не может.
# При бампе версии правь оба места.
VERSION="0.5.0"
VERSION="0.5.1"
CHANNEL="${CHANNEL:-Beta}"
# Откуда брать архив и как называется продукт. По умолчанию — релизы на GitHub; источник