refactor(api): implement abort signal handling in API requests across multiple components to improve cancellation of ongoing requests and prevent memory leaks
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m12s

This commit is contained in:
2026-02-22 23:53:15 +07:00
parent 3fda43cf2c
commit a14f10d048
17 changed files with 278 additions and 128 deletions
+20 -10
View File
@@ -157,13 +157,18 @@ function MikrotikBackupsManager() {
const backupPageSize = 10;
useEffect(() => {
fetchServers();
fetchUiSettings();
const controller = new AbortController();
const signal = controller.signal;
fetchServers(signal);
fetchUiSettings(signal);
return () => controller.abort();
}, []);
useEffect(() => {
if (selectedServerId) {
fetchBackups(selectedServerId);
const controller = new AbortController();
fetchBackups(selectedServerId, controller.signal);
return () => controller.abort();
} else {
setBackups([]);
setSelectedKeys([]);
@@ -314,10 +319,11 @@ function MikrotikBackupsManager() {
setBackupServerIds(new Set());
};
const fetchServers = async () => {
const fetchServers = async (abortSignal) => {
const opts = abortSignal ? { signal: abortSignal } : {};
try {
setLoadingServers(true);
const res = await api.get('/servers');
const res = await api.get('/servers', opts);
const list = Array.isArray(res.data) ? res.data : [];
setServers(list);
const firstJumphost = list.find((s) => String(s.type || '').toLowerCase() === 'jumphost');
@@ -325,6 +331,7 @@ function MikrotikBackupsManager() {
setSelectedServerId(makeServerId(firstJumphost));
}
} catch (err) {
if (err?.name === 'CanceledError' || err?.name === 'AbortError' || err?.code === 'ERR_CANCELED') return;
console.error('Error fetching servers for backups:', err);
notify.error('Не удалось загрузить список серверов для бэкапов');
} finally {
@@ -332,9 +339,10 @@ function MikrotikBackupsManager() {
}
};
const fetchUiSettings = async () => {
const fetchUiSettings = async (abortSignal) => {
const opts = abortSignal ? { signal: abortSignal } : {};
try {
const res = await api.get('/ui-settings');
const res = await api.get('/ui-settings', opts);
const data = res?.data || {};
setUiSettings(data);
const list = Array.isArray(data.mikrotikBackupServers) ? data.mikrotikBackupServers : [];
@@ -342,21 +350,23 @@ function MikrotikBackupsManager() {
const e = res?.headers?.etag || res?.headers?.ETag || '';
setUiSettingsEtag(e ? String(e) : '');
} catch (err) {
if (err?.name === 'CanceledError' || err?.name === 'AbortError' || err?.code === 'ERR_CANCELED') return;
console.error('Error fetching UI settings for backups:', err);
// мягко игнорируем, просто будут дефолты
}
};
const fetchBackups = async (serverId) => {
const fetchBackups = async (serverId, abortSignal) => {
if (!serverId) return;
const opts = abortSignal ? { signal: abortSignal } : {};
try {
setLoadingBackups(true);
setSelectedKeys([]);
setDiffResult(null);
const res = await api.get('/mikrotik/backups', { params: { serverId } });
const res = await api.get('/mikrotik/backups', { params: { serverId }, ...opts });
const items = res.data?.items || [];
setBackups(items);
} catch (err) {
if (err?.name === 'CanceledError' || err?.name === 'AbortError' || err?.code === 'ERR_CANCELED') return;
console.error('Error fetching backups:', err);
notify.error('Не удалось загрузить список бэкапов MikroTik');
} finally {