Add AWG_GEN/AWG_PIN switches and surface module version

Upstream AmneziaWG 3.x is still shifting under active development
(unanswered kernel-module issues around header protection and
handshake-without-data regressions), so give operators an explicit
opt-out/opt-in instead of always auto-selecting 3.1, and pin the
installed package so unattended-upgrades can't swap the module under
a live server. Surface the running module/tools version and protocol
generation through /health, the CLI status command, and the panel's
server card so a mismatch is visible instead of silent.
This commit is contained in:
2026-08-25 14:57:41 +03:00
parent 6fdacd71c3
commit c0e19f284d
8 changed files with 180 additions and 49 deletions
+7
View File
@@ -406,6 +406,13 @@ body {
color: var(--neutral-text);
line-height: 1.2;
}
.server-gen-wrap { align-self: flex-start; margin-top: 2px; }
.server-gen-chip {
align-self: flex-start;
margin-top: 2px;
font-size: 11px;
padding: 2px 8px;
}
.server-card-peers {
font-size: 12px;
color: var(--neutral-text);
+4 -1
View File
@@ -36,7 +36,10 @@ export default function App() {
// вкладок (юзеры, ключи) грузят сами вкладки.
const startSession = useCallback(async (tok: string) => {
const { data: h } = await axios.get('/health', { headers: { Authorization: `Bearer ${tok}` } });
setServerInfo({ name: h.server || 'VPN', ip: h.ip || '', peers: h.awg?.peers ?? 0, gen: h.gen || '2' });
setServerInfo({
name: h.server || 'VPN', ip: h.ip || '', peers: h.awg?.peers ?? 0, gen: h.gen || '2',
mod: h.awg?.module ?? '', tools: h.awg?.tools ?? '',
});
setStatusText('online:' + (h.server || 'ok') + ' · peers: ' + (h.awg?.peers ?? '?'));
}, []);
+11 -1
View File
@@ -1,7 +1,7 @@
// Copyright (c) 2026 Ivan Vasilev
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
import { type ServerInfo } from '../lib/shared';
import { type ServerInfo, genLabel } from '../lib/shared';
import { IcoPlus, IcoRefresh } from './icons';
// Сервер-бар: карточка активного сервера + «Добавить сервер» + (на мобильной)
@@ -38,6 +38,16 @@ export default function ServerBar({
<div className="server-card-info">
<span className="server-card-name">{serverInfo.name}</span>
<span className="server-card-ip">{serverInfo.ip}</span>
{(serverInfo.mod || serverInfo.tools) ? (
<span
className="tip-wrap server-gen-wrap"
data-tip={`модуль ${serverInfo.mod || '?'} · tools ${serverInfo.tools || '?'}`}
>
<span className="chip chip--offline server-gen-chip">{genLabel(serverInfo.gen)}</span>
</span>
) : (
<span className="chip chip--offline server-gen-chip">{genLabel(serverInfo.gen)}</span>
)}
</div>
<span className="server-card-peers">{serverInfo.peers} peers</span>
</div>
+4
View File
@@ -21,7 +21,11 @@ export interface ServerInfo {
ip: string;
peers: number;
gen: AwgGen;
mod: string; // версия загруженного модуля amneziawg, из /health awg.module
tools: string; // версия awg-tools, из /health awg.tools
}
// На проводе (users.key_gen, /health gen) поколение — '2', человеку показываем '2.0'.
export const genLabel = (g: AwgGen): string => (g === '3.1' ? 'AWG 3.1' : 'AWG 2.0');
export interface ApiKey {
id: number;
label: string;
+5 -5
View File
@@ -4,7 +4,7 @@
import { useState, useEffect, useRef, useCallback } from 'react';
import QRCode from 'qrcode';
import {
apiFetch, vpnKeyToConf, downloadFile, copyText, bytes, timeAgo,
apiFetch, vpnKeyToConf, downloadFile, copyText, bytes, timeAgo, genLabel,
type User, type PageProps, type AwgGen,
} from '../../lib/shared';
import { IcoPlus, IcoRefresh, IcoQR, IcoTrash, IcoGlobe } from '../../components/icons';
@@ -50,7 +50,7 @@ export default function UsersPage({ token, showMsg }: PageProps) {
// IP и ключевая пара сохраняются, но клиентам нужно заново импортировать ключ.
const reissueKeys = useCallback(async () => {
if (!confirm(
`Перевыпустить ключи (${stale.length} шт.) на поколении ${serverGen}?\n\n` +
`Перевыпустить ключи (${stale.length} шт.) на поколении ${genLabel(serverGen)}?\n\n` +
'Старые ключи перестанут работать — всем придётся импортировать ключ заново.',
)) return;
try {
@@ -162,11 +162,11 @@ export default function UsersPage({ token, showMsg }: PageProps) {
{u.key_gen !== serverGen && (
<span
className="tip-wrap user-gen"
data-tip={`Ключ выдан на AWG ${u.key_gen}, сервер работает на ${serverGen}`}
data-tip={`Ключ выдан на ${genLabel(u.key_gen)}, сервер работает на ${genLabel(serverGen)}`}
>
<span className="chip chip--error">
<span className="chip-dot" />
AWG {u.key_gen}
{genLabel(u.key_gen)}
</span>
</span>
)}
@@ -207,7 +207,7 @@ export default function UsersPage({ token, showMsg }: PageProps) {
{u.key_gen !== serverGen && (
<span className="chip chip--error">
<span className="chip-dot" />
AWG {u.key_gen}
{genLabel(u.key_gen)}
</span>
)}
</div>