Add --capture disabled mode + capture-mode status in PWA
Server: - --capture accepts 'disabled'; no backend is created, capture_mode exposed - /api/status returns capture_mode; capture start/stop return 403 when disabled; latest/status/delete_session are None-safe PWA: - captureMode state; conn-status shows server version - new capture-mode line (Settings) explaining gsmtap/direct/disabled - hide capture + New capture UI when disabled; trace empty-state shows 'Capture unavailable' - 7 new EN/RU i18n keys Version 1.2.0 (server + PWA); tests: 111.
This commit is contained in:
1 parent
b900a8a3e1
commit
24d0decd51
7 files changed
+130
-22
No files matched your search
+50
-5
@@ -171,6 +171,8 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
|
||||
.settings-view .conn-status.connected { background: var(--accent-bg); color: var(--green); }
|
||||
.settings-view .conn-status.disconnected { background: var(--warn-bg); color: var(--error); }
|
||||
.conn-hint { font-size: 12px; color: var(--text-muted); margin: 8px 0 0; line-height: 1.5; }
|
||||
.capture-mode { font-size: 12px; color: var(--text-muted); margin: 8px 0 0; line-height: 1.5; }
|
||||
.capture-mode:empty { display: none; }
|
||||
.version-warn { font-size: 12px; color: var(--warn); margin: 8px 0 0; line-height: 1.5; }
|
||||
.version-warn:empty { display: none; }
|
||||
.conn-instructions { margin-top: 16px; font-size: 13px; color: var(--text-faint); line-height: 1.7; }
|
||||
@@ -272,6 +274,7 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
|
||||
<div class="conn-status disconnected" id="conn-status">
|
||||
⬤ Not connected — ensure the server is running on the URL above
|
||||
</div>
|
||||
<div class="capture-mode" id="capture-mode"></div>
|
||||
<div class="version-warn" id="version-warn"></div>
|
||||
<div class="conn-hint" id="conn-hint"></div>
|
||||
<div class="conn-instructions" id="conn-instructions"></div>
|
||||
@@ -313,6 +316,7 @@ const STATE = {
|
||||
typeCounts: [],
|
||||
pendingDeleteId: null,
|
||||
serverAvailable: false,
|
||||
captureMode: '',
|
||||
};
|
||||
|
||||
const API_BASE = () => STATE.serverUrl;
|
||||
@@ -760,9 +764,14 @@ function renderMessages() {
|
||||
populateFilterOptions();
|
||||
|
||||
if (!STATE.captureActive && !STATE.sessionViewId) {
|
||||
list.innerHTML = `<div class="empty-state"><p>${t('No capture active')}</p>
|
||||
<button id="trace-start-btn">${t('Start Capture')}</button></div>`;
|
||||
$('#trace-start-btn', list).addEventListener('click', startCapture);
|
||||
if (STATE.captureMode === 'disabled') {
|
||||
list.innerHTML = `<div class="empty-state"><p>${t('Capture unavailable')}</p>
|
||||
<p style="font-size:12px;">${t('Open a saved session from the Sessions tab.')}</p></div>`;
|
||||
} else {
|
||||
list.innerHTML = `<div class="empty-state"><p>${t('No capture active')}</p>
|
||||
<button id="trace-start-btn">${t('Start Capture')}</button></div>`;
|
||||
$('#trace-start-btn', list).addEventListener('click', startCapture);
|
||||
}
|
||||
updateStatus();
|
||||
return;
|
||||
}
|
||||
@@ -837,6 +846,7 @@ function stopPolling() {
|
||||
|
||||
// ──────────────────── Capture ────────────────────
|
||||
async function startCapture() {
|
||||
if (STATE.captureMode === 'disabled') return;
|
||||
try {
|
||||
const data = await API.startCapture();
|
||||
STATE.captureActive = true;
|
||||
@@ -864,7 +874,14 @@ async function stopCapture() {
|
||||
|
||||
function updateCaptureButton() {
|
||||
const btn = el('capture-btn');
|
||||
const newCapture = el('new-capture-btn');
|
||||
btn.classList.remove('stop', 'connect');
|
||||
const captureOff = STATE.captureMode === 'disabled' && STATE.serverAvailable;
|
||||
newCapture.style.display = captureOff ? 'none' : '';
|
||||
if (captureOff) {
|
||||
btn.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
if (STATE.sessionViewId && !STATE.captureActive) {
|
||||
btn.style.display = 'none';
|
||||
return;
|
||||
@@ -1062,6 +1079,23 @@ function updateVersionWarn(serverVersion) {
|
||||
elv.textContent = '';
|
||||
}
|
||||
|
||||
function updateCaptureModeLine() {
|
||||
const elm = el('capture-mode');
|
||||
switch (STATE.captureMode) {
|
||||
case 'gsmtap':
|
||||
elm.textContent = t('Capture mode: GSMTAP — the server is listening for incoming UDP/GSMTAP traffic. Ensure a GSMTAP source (e.g. simtrace2-pysniff or simtrace2-sniff) is feeding the server.');
|
||||
break;
|
||||
case 'direct':
|
||||
elm.textContent = t('Capture mode: direct — the server captures from SIMtrace2 hardware. Keep the device connected.');
|
||||
break;
|
||||
case 'disabled':
|
||||
elm.textContent = t('Capture is disabled — only pre-captured traces (saved sessions / PCAP import) are available.');
|
||||
break;
|
||||
default:
|
||||
elm.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
function connectServer() {
|
||||
if (_connTimer) return;
|
||||
_connTimer = setInterval(checkConnection, 5000);
|
||||
@@ -1076,6 +1110,7 @@ function disconnectServer() {
|
||||
STATE.serverAvailable = false;
|
||||
el('conn-status').textContent = '⬤ ' + t('Disconnected — click Connect to resume');
|
||||
el('conn-status').className = 'conn-status disconnected';
|
||||
el('capture-mode').textContent = '';
|
||||
el('version-warn').textContent = '';
|
||||
el('conn-hint').textContent = '';
|
||||
el('conn-instructions').innerHTML = '';
|
||||
@@ -1096,9 +1131,11 @@ async function checkConnection() {
|
||||
try {
|
||||
const data = await API.status();
|
||||
STATE.serverAvailable = true;
|
||||
el('conn-status').textContent = '⬤ ' + t('Connected') + ' — ' + data.server;
|
||||
STATE.captureMode = data.capture_mode || '';
|
||||
el('conn-status').textContent = '⬤ ' + t('Connected') + ' — ' + data.server + ' v' + (data.version || '?');
|
||||
el('conn-status').className = 'conn-status connected';
|
||||
updateVersionWarn(data.version);
|
||||
updateCaptureModeLine();
|
||||
el('conn-hint').textContent = '';
|
||||
el('conn-instructions').innerHTML = '';
|
||||
el('connect-btn').textContent = t('Disconnect');
|
||||
@@ -1112,8 +1149,10 @@ async function checkConnection() {
|
||||
updateCaptureButton();
|
||||
} catch (e) {
|
||||
STATE.serverAvailable = false;
|
||||
STATE.captureMode = '';
|
||||
el('conn-status').textContent = '⬤ ' + t('Not connected — ensure the server is running on the URL above');
|
||||
el('conn-status').className = 'conn-status disconnected';
|
||||
el('capture-mode').textContent = '';
|
||||
el('version-warn').textContent = '';
|
||||
const url = STATE.serverUrl || location.origin;
|
||||
el('conn-hint').textContent = t(isLikelyBlocked() ? HINT_BLOCKED : HINT_UNREACHABLE).replace('{url}', url);
|
||||
@@ -1167,7 +1206,7 @@ function onFilterChange() {
|
||||
}
|
||||
|
||||
// ──────────────────── Versioning ────────────────────
|
||||
const PWA_VERSION = '1.1.0';
|
||||
const PWA_VERSION = '1.2.0';
|
||||
const MIN_SERVER_VERSION = '1.0.0';
|
||||
|
||||
function parseVersion(v) {
|
||||
@@ -1261,6 +1300,12 @@ const LANG_RU = {
|
||||
'Server version unknown — update simtrace2-pysniff-server.': 'Версия сервера неизвестна — обновите simtrace2-pysniff-server.',
|
||||
'Server version {v} is outdated. Please update simtrace2-pysniff-server.': 'Версия сервера {v} устарела. Обновите simtrace2-pysniff-server.',
|
||||
'Server version {v} is newer than expected. Update simtrace2-pysniff for full compatibility.': 'Версия сервера {v} новее ожидаемой. Обновите simtrace2-pysniff для полной совместимости.',
|
||||
'Capture mode: GSMTAP — the server is listening for incoming UDP/GSMTAP traffic. Ensure a GSMTAP source (e.g. simtrace2-pysniff or simtrace2-sniff) is feeding the server.': 'Режим захвата: GSMTAP — сервер ожидает входящий UDP/GSMTAP-трафик. Убедитесь, что источник GSMTAP (например, simtrace2-pysniff или simtrace2-sniff) передаёт данные серверу.',
|
||||
'Capture mode: direct — the server captures from SIMtrace2 hardware. Keep the device connected.': 'Режим захвата: direct — сервер захватывает данные с устройства SIMtrace2. Не отключайте устройство.',
|
||||
'Capture is disabled — only pre-captured traces (saved sessions / PCAP import) are available.': 'Захват отключён — доступны только ранее записанные трассировки (сохранённые сессии / импорт PCAP).',
|
||||
'Capture unavailable': 'Захват недоступен',
|
||||
'Open a saved session from the Sessions tab.': 'Откройте сохранённую сессию на вкладке «Сессии».',
|
||||
'Capture disabled': 'Захват отключён',
|
||||
};
|
||||
|
||||
function t(text) {
|
||||
|
||||
Reference in new issue
Block a user