diff --git a/backend/server.js b/backend/server.js index 62c5841..79e8fae 100644 --- a/backend/server.js +++ b/backend/server.js @@ -500,7 +500,7 @@ app.get('/api/domains', async (req, res) => { const { total } = await streamPaginatedText({ key: FILE_KEY, q, offset: 0, limit: 0, mapLine: () => ({}) }); setCountOnlyCache(cacheKey, total); return res.json(format === 'std' ? { items: [], total, meta: {} } : { total }); - } else if (typeof limit !== 'undefined') { + } else if (Number(limit) > 0) { const { items, total } = await streamPaginatedText({ key: FILE_KEY, q, @@ -596,7 +596,7 @@ app.get('/api/asns', async (req, res) => { const { total } = await streamPaginatedText({ key: 'bgp_data/asns.txt', q, offset: 0, limit: 0, mapLine: () => ({}) }); setCountOnlyCache(cacheKey, total); return res.json(format === 'std' ? { items: [], total, meta: {} } : { total }); - } else if (limit !== undefined) { + } else if (Number(limit) > 0) { const { items, total } = await streamPaginatedText({ key: 'bgp_data/asns.txt', q, @@ -693,7 +693,7 @@ app.get('/api/domains-new', async (req, res) => { const { total } = await streamPaginatedText({ key: 'bgp_data/domains_community.txt', q, offset: 0, limit: 0, mapLine: () => ({}) }); setCountOnlyCache(cacheKey, total); return res.json(format === 'std' ? { items: [], total, meta: {} } : { total }); - } else if (limit !== undefined) { + } else if (Number(limit) > 0) { const { items, total } = await streamPaginatedText({ key: 'bgp_data/domains_community.txt', q, @@ -790,7 +790,7 @@ app.get('/api/ip-ranges', async (req, res) => { const { total } = await streamPaginatedText({ key: 'bgp_data/ips.txt', q, offset: 0, limit: 0, mapLine: () => ({}) }); setCountOnlyCache(cacheKey, total); return res.json(format === 'std' ? { items: [], total, meta: {} } : { total }); - } else if (limit !== undefined) { + } else if (Number(limit) > 0) { const { items, total } = await streamPaginatedText({ key: 'bgp_data/ips.txt', q, diff --git a/frontend/src/Dashboard.jsx b/frontend/src/Dashboard.jsx index 92bb712..64405d2 100644 --- a/frontend/src/Dashboard.jsx +++ b/frontend/src/Dashboard.jsx @@ -102,7 +102,8 @@ function Dashboard() { const domains = []; const ipRanges = []; const asns = []; - const servers = serversRes.status === 'fulfilled' ? serversRes.value.data : []; + const serversRaw = serversRes.status === 'fulfilled' ? serversRes.value.data : []; + const servers = Array.isArray(serversRaw) ? serversRaw : []; setRaw({ domains, ipRanges, asns, servers }); const countries = new Set(servers.map(server => server.country).filter(Boolean)); const providers = new Set(servers.map(server => server.provider).filter(Boolean)); diff --git a/frontend/src/lib/api.js b/frontend/src/lib/api.js index 58703a9..58983c4 100644 --- a/frontend/src/lib/api.js +++ b/frontend/src/lib/api.js @@ -32,15 +32,20 @@ api.interceptors.response.use( if (method === 'get') { const key = buildCacheKey(response.config); const etag = response.headers?.etag; - if (etag) { - responseCache.set(key, { etag, data: response.data, headers: response.headers }); - } - // Если пришёл 304 (на всякий случай, axios обычно не кидает в success 304), подменим данными из кеша + // Если 304 — всегда пробуем вернуть кеш, не трогая response.data if (response.status === 304) { const cached = responseCache.get(key); if (cached) { return { ...response, status: 200, data: cached.data, headers: { ...cached.headers, 'x-from-cache': '1' } }; } + // нет кеша — вернём пустые семантически корректные данные (чтобы не падали .map) + // вызывающий код должен ожидать типы, поэтому лучше не подменять тип неожиданно. + // Просто пропустим дальше как есть — до второго интерсептора и обработчиков. + return response; + } + // Не 304: обновляем кеш, но только если есть валидный etag + if (etag) { + responseCache.set(key, { etag, data: response.data, headers: response.headers }); } } } catch {}