feat: Refactor S3 last modified endpoint and Dashboard data fetching to handle errors gracefully
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m7s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m7s
This commit is contained in:
+29
-9
@@ -436,22 +436,42 @@ app.post('/api/filters', async (req, res) => {
|
|||||||
// Новый эндпоинт для получения дат последнего изменения файлов S3
|
// Новый эндпоинт для получения дат последнего изменения файлов S3
|
||||||
app.get('/api/s3/last-modified', async (req, res) => {
|
app.get('/api/s3/last-modified', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const [domainsHead, domainsNewHead, asnsHead, serversHead, filtersHead] = await Promise.all([
|
const results = await Promise.allSettled([
|
||||||
s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/domains.txt' }).promise(),
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/domains.txt' }).promise(),
|
||||||
s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/domains_community.txt' }).promise(),
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/domains_community.txt' }).promise(),
|
||||||
s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/asns.txt' }).promise(),
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'bgp_data/asns.txt' }).promise(),
|
||||||
s3.headObject({ Bucket: BUCKET_NAME, Key: 'servers.json' }).promise(),
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'servers.json' }).promise(),
|
||||||
s3.headObject({ Bucket: BUCKET_NAME, Key: 'filters.json' }).promise()
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'filters.json' }).promise()
|
||||||
]);
|
]);
|
||||||
res.json({
|
|
||||||
domainsLastModified: domainsHead.LastModified ? domainsHead.LastModified.toISOString() : null,
|
const response = {
|
||||||
domainsNewLastModified: domainsNewHead.LastModified ? domainsNewHead.LastModified.toISOString() : null,
|
domainsLastModified: null,
|
||||||
asnsLastModified: asnsHead.LastModified ? asnsHead.LastModified.toISOString() : null,
|
domainsNewLastModified: null,
|
||||||
serversLastModified: serversHead.LastModified ? serversHead.LastModified.toISOString() : null,
|
asnsLastModified: null,
|
||||||
filtersLastModified: filtersHead.LastModified ? filtersHead.LastModified.toISOString() : null
|
serversLastModified: null,
|
||||||
});
|
filtersLastModified: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// Обрабатываем результаты
|
||||||
|
if (results[0].status === 'fulfilled') {
|
||||||
|
response.domainsLastModified = results[0].value.LastModified ? results[0].value.LastModified.toISOString() : null;
|
||||||
|
}
|
||||||
|
if (results[1].status === 'fulfilled') {
|
||||||
|
response.domainsNewLastModified = results[1].value.LastModified ? results[1].value.LastModified.toISOString() : null;
|
||||||
|
}
|
||||||
|
if (results[2].status === 'fulfilled') {
|
||||||
|
response.asnsLastModified = results[2].value.LastModified ? results[2].value.LastModified.toISOString() : null;
|
||||||
|
}
|
||||||
|
if (results[3].status === 'fulfilled') {
|
||||||
|
response.serversLastModified = results[3].value.LastModified ? results[3].value.LastModified.toISOString() : null;
|
||||||
|
}
|
||||||
|
if (results[4].status === 'fulfilled') {
|
||||||
|
response.filtersLastModified = results[4].value.LastModified ? results[4].value.LastModified.toISOString() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(response);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error('Error fetching last modified dates from S3:', error);
|
||||||
res.status(500).send('Error fetching last modified dates from S3');
|
res.status(500).send('Error fetching last modified dates from S3');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+31
-12
@@ -78,7 +78,8 @@ function Dashboard() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
const [domainsRes, ipRangesRes, asnsRes, serversRes, s3Res] = await Promise.all([
|
// Загружаем данные с обработкой ошибок для каждого endpoint
|
||||||
|
const results = await Promise.allSettled([
|
||||||
axios.get('/api/domains'),
|
axios.get('/api/domains'),
|
||||||
axios.get('/api/ip-ranges'),
|
axios.get('/api/ip-ranges'),
|
||||||
axios.get('/api/asns'),
|
axios.get('/api/asns'),
|
||||||
@@ -86,25 +87,43 @@ function Dashboard() {
|
|||||||
axios.get('/api/s3/last-modified')
|
axios.get('/api/s3/last-modified')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Подсчет уникальных стран и провайдеров из серверов
|
// Обрабатываем результаты
|
||||||
const servers = serversRes.data;
|
const domainsRes = results[0];
|
||||||
|
const ipRangesRes = results[1];
|
||||||
|
const asnsRes = results[2];
|
||||||
|
const serversRes = results[3];
|
||||||
|
const s3Res = results[4];
|
||||||
|
|
||||||
|
// Получаем данные серверов для подсчета дополнительной статистики
|
||||||
|
const servers = serversRes.status === 'fulfilled' ? serversRes.value.data : [];
|
||||||
const countries = new Set(servers.map(server => server.country).filter(Boolean));
|
const countries = new Set(servers.map(server => server.country).filter(Boolean));
|
||||||
const providers = new Set(servers.map(server => server.provider).filter(Boolean));
|
const providers = new Set(servers.map(server => server.provider).filter(Boolean));
|
||||||
const onlineServers = servers.filter(server => server.status === 'Онлайн').length;
|
const onlineServers = servers.filter(server => server.status === 'Онлайн').length;
|
||||||
|
|
||||||
|
// Получаем дату последнего обновления
|
||||||
|
const lastModified = s3Res.status === 'fulfilled' && s3Res.value.data?.domainsLastModified
|
||||||
|
? new Date(s3Res.value.data.domainsLastModified).toLocaleString()
|
||||||
|
: new Date().toLocaleString();
|
||||||
|
|
||||||
setStats({
|
setStats({
|
||||||
domainsCount: domainsRes.data.length,
|
domainsCount: domainsRes.status === 'fulfilled' ? domainsRes.value.data.length : 0,
|
||||||
ipRangesCount: ipRangesRes.data.length,
|
ipRangesCount: ipRangesRes.status === 'fulfilled' ? ipRangesRes.value.data.length : 0,
|
||||||
asnsCount: asnsRes.data.length,
|
asnsCount: asnsRes.status === 'fulfilled' ? asnsRes.value.data.length : 0,
|
||||||
serversCount: servers.length,
|
serversCount: servers.length,
|
||||||
lastModified: s3Res.data?.domainsLastModified || null,
|
lastModified,
|
||||||
countriesCount: countries.size,
|
countriesCount: countries.size,
|
||||||
providersCount: providers.size,
|
providersCount: providers.size,
|
||||||
onlineServers,
|
onlineServers,
|
||||||
totalServers: servers.length
|
totalServers: servers.length
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Проверяем, есть ли ошибки
|
||||||
|
const errors = results.filter(result => result.status === 'rejected');
|
||||||
|
if (errors.length > 0) {
|
||||||
|
console.warn('Некоторые API недоступны:', errors.map(e => e.reason?.message));
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Ошибка загрузки статистики:', e);
|
console.error('Критическая ошибка загрузки статистики:', e);
|
||||||
setError('Не удалось загрузить статистику');
|
setError('Не удалось загрузить статистику');
|
||||||
setStats({
|
setStats({
|
||||||
domainsCount: null,
|
domainsCount: null,
|
||||||
@@ -250,11 +269,11 @@ function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="col-md-2">
|
<div className="col-md-2">
|
||||||
<MetricCard
|
<MetricCard
|
||||||
title="База данных"
|
title="API статус"
|
||||||
value="ОК"
|
value={loading ? '...' : (error ? 'Ошибка' : 'ОК')}
|
||||||
icon={IconDatabase}
|
icon={IconDatabase}
|
||||||
color="success"
|
color={loading ? 'secondary' : (error ? 'danger' : 'success')}
|
||||||
description="Все системы в норме"
|
description={error ? 'Проблемы с подключением' : 'Все API работают'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user