feat(InterfaceSpeedTest, MikrotikConfigRoutes): enhance speed test functionality with cache handling and force refresh options
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m53s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m53s
This commit is contained in:
@@ -851,7 +851,7 @@ function parseSpeedToBps(val) {
|
|||||||
*/
|
*/
|
||||||
async function speedTestViaTunnel(req, res) {
|
async function speedTestViaTunnel(req, res) {
|
||||||
try {
|
try {
|
||||||
const { serverId, interfaceName } = req.body || {};
|
const { serverId, interfaceName, cacheOnly, forceRefresh } = req.body || {};
|
||||||
let { durationSeconds } = req.body || {};
|
let { durationSeconds } = req.body || {};
|
||||||
|
|
||||||
if (!serverId || !interfaceName) {
|
if (!serverId || !interfaceName) {
|
||||||
@@ -864,6 +864,39 @@ async function speedTestViaTunnel(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uiSettings = await loadUiSettings();
|
const uiSettings = await loadUiSettings();
|
||||||
|
const cacheMinutes = Math.max(
|
||||||
|
0,
|
||||||
|
parseInt(uiSettings.interfaceSpeedTestCacheMinutes, 10) || 0
|
||||||
|
);
|
||||||
|
const cacheKey =
|
||||||
|
cacheMinutes > 0
|
||||||
|
? speedTestCacheKey(serverId, interfaceName)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// При выборе сервера/интерфейса — только кеш. При нажатии «Замерить» — принудительно новый замер (forceRefresh).
|
||||||
|
const skipCacheRead = forceRefresh === true;
|
||||||
|
|
||||||
|
if (!skipCacheRead && cacheKey) {
|
||||||
|
try {
|
||||||
|
const raw = await readS3TextObject(cacheKey).catch(() => null);
|
||||||
|
if (raw?.body) {
|
||||||
|
const cached = JSON.parse(raw.body);
|
||||||
|
const cachedAt =
|
||||||
|
typeof cached.cachedAt === 'number' ? cached.cachedAt : 0;
|
||||||
|
const ttlMs = cacheMinutes * 60 * 1000;
|
||||||
|
if (cachedAt && Date.now() - cachedAt < ttlMs) {
|
||||||
|
return res.json({ ...cached, cached: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
// Если кеш не прочитался — продолжаем без него
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheOnly === true) {
|
||||||
|
return res.json({ ok: false, notInCache: true });
|
||||||
|
}
|
||||||
|
|
||||||
const defaultDuration = Math.max(
|
const defaultDuration = Math.max(
|
||||||
1,
|
1,
|
||||||
Math.min(
|
Math.min(
|
||||||
@@ -882,32 +915,6 @@ async function speedTestViaTunnel(req, res) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const cacheMinutes = Math.max(
|
|
||||||
0,
|
|
||||||
parseInt(uiSettings.interfaceSpeedTestCacheMinutes, 10) || 0
|
|
||||||
);
|
|
||||||
const cacheKey =
|
|
||||||
cacheMinutes > 0
|
|
||||||
? speedTestCacheKey(serverId, interfaceName)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (cacheKey) {
|
|
||||||
try {
|
|
||||||
const raw = await readS3TextObject(cacheKey).catch(() => null);
|
|
||||||
if (raw?.body) {
|
|
||||||
const cached = JSON.parse(raw.body);
|
|
||||||
const cachedAt =
|
|
||||||
typeof cached.cachedAt === 'number' ? cached.cachedAt : 0;
|
|
||||||
const ttlMs = cacheMinutes * 60 * 1000;
|
|
||||||
if (cachedAt && Date.now() - cachedAt < ttlMs) {
|
|
||||||
return res.json({ ...cached, cached: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
// Если кеш не прочитался — продолжаем без него
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = await loadNetworkConfig();
|
const config = await loadNetworkConfig();
|
||||||
const tunnelIfaces = Array.isArray(config.tunnelInterfaces)
|
const tunnelIfaces = Array.isArray(config.tunnelInterfaces)
|
||||||
? config.tunnelInterfaces
|
? config.tunnelInterfaces
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import api from './lib/api.js';
|
import api from './lib/api.js';
|
||||||
import { useNotify } from './components/NotifyProvider.jsx';
|
import { useNotify } from './components/NotifyProvider.jsx';
|
||||||
import PageHeader from './components/PageHeader.jsx';
|
import PageHeader from './components/PageHeader.jsx';
|
||||||
@@ -167,6 +167,33 @@ export default function InterfaceSpeedTest() {
|
|||||||
return `${mbps.toFixed(2)} Мбит/с`;
|
return `${mbps.toFixed(2)} Мбит/с`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Запросить только кеш (без замера). При выборе сервера/интерфейса показываем кеш, если есть. */
|
||||||
|
const fetchCachedResult = useCallback(async () => {
|
||||||
|
if (!serverId || !selectedInterfaceName) {
|
||||||
|
setSpeedResult(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await api.post('/mikrotik/speed-test', {
|
||||||
|
serverId,
|
||||||
|
interfaceName: selectedInterfaceName,
|
||||||
|
cacheOnly: true,
|
||||||
|
});
|
||||||
|
const data = res?.data || {};
|
||||||
|
if (data.ok !== false && !data.notInCache && (data.tcpDownloadBps != null || data.tcpUploadBps != null)) {
|
||||||
|
setSpeedResult(data);
|
||||||
|
} else {
|
||||||
|
setSpeedResult(null);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setSpeedResult(null);
|
||||||
|
}
|
||||||
|
}, [serverId, selectedInterfaceName]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchCachedResult();
|
||||||
|
}, [fetchCachedResult]);
|
||||||
|
|
||||||
const handleRunTest = async () => {
|
const handleRunTest = async () => {
|
||||||
if (!serverId) {
|
if (!serverId) {
|
||||||
notify.error('Выберите сервер (jumphost/home) для замера скорости');
|
notify.error('Выберите сервер (jumphost/home) для замера скорости');
|
||||||
@@ -185,9 +212,9 @@ export default function InterfaceSpeedTest() {
|
|||||||
serverId,
|
serverId,
|
||||||
interfaceName: selectedInterfaceName,
|
interfaceName: selectedInterfaceName,
|
||||||
durationSeconds: speedSettings.durationSeconds,
|
durationSeconds: speedSettings.durationSeconds,
|
||||||
|
forceRefresh: true,
|
||||||
};
|
};
|
||||||
const res = await api.post('/mikrotik/speed-test', body, {
|
const res = await api.post('/mikrotik/speed-test', body, {
|
||||||
// speed-test может занимать дольше стандартных 30 секунд
|
|
||||||
timeout: 120000,
|
timeout: 120000,
|
||||||
});
|
});
|
||||||
const data = res?.data || {};
|
const data = res?.data || {};
|
||||||
|
|||||||
Reference in New Issue
Block a user