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

This commit is contained in:
2025-08-05 21:39:58 +07:00
parent 7c6ceb66bc
commit 6dee0fa0b4
2 changed files with 60 additions and 21 deletions
+29 -9
View File
@@ -436,22 +436,42 @@ app.post('/api/filters', async (req, res) => {
// Новый эндпоинт для получения дат последнего изменения файлов S3
app.get('/api/s3/last-modified', async (req, res) => {
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_community.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: 'filters.json' }).promise()
]);
res.json({
domainsLastModified: domainsHead.LastModified ? domainsHead.LastModified.toISOString() : null,
domainsNewLastModified: domainsNewHead.LastModified ? domainsNewHead.LastModified.toISOString() : null,
asnsLastModified: asnsHead.LastModified ? asnsHead.LastModified.toISOString() : null,
serversLastModified: serversHead.LastModified ? serversHead.LastModified.toISOString() : null,
filtersLastModified: filtersHead.LastModified ? filtersHead.LastModified.toISOString() : null
});
const response = {
domainsLastModified: null,
domainsNewLastModified: null,
asnsLastModified: 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) {
console.error(error);
console.error('Error fetching last modified dates from S3:', error);
res.status(500).send('Error fetching last modified dates from S3');
}
});
+31 -12
View File
@@ -78,7 +78,8 @@ function Dashboard() {
setLoading(true);
setError(null);
try {
const [domainsRes, ipRangesRes, asnsRes, serversRes, s3Res] = await Promise.all([
// Загружаем данные с обработкой ошибок для каждого endpoint
const results = await Promise.allSettled([
axios.get('/api/domains'),
axios.get('/api/ip-ranges'),
axios.get('/api/asns'),
@@ -86,25 +87,43 @@ function Dashboard() {
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 providers = new Set(servers.map(server => server.provider).filter(Boolean));
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({
domainsCount: domainsRes.data.length,
ipRangesCount: ipRangesRes.data.length,
asnsCount: asnsRes.data.length,
domainsCount: domainsRes.status === 'fulfilled' ? domainsRes.value.data.length : 0,
ipRangesCount: ipRangesRes.status === 'fulfilled' ? ipRangesRes.value.data.length : 0,
asnsCount: asnsRes.status === 'fulfilled' ? asnsRes.value.data.length : 0,
serversCount: servers.length,
lastModified: s3Res.data?.domainsLastModified || null,
lastModified,
countriesCount: countries.size,
providersCount: providers.size,
onlineServers,
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) {
console.error('Ошибка загрузки статистики:', e);
console.error('Критическая ошибка загрузки статистики:', e);
setError('Не удалось загрузить статистику');
setStats({
domainsCount: null,
@@ -250,11 +269,11 @@ function Dashboard() {
</div>
<div className="col-md-2">
<MetricCard
title="База данных"
value="ОК"
title="API статус"
value={loading ? '...' : (error ? 'Ошибка' : 'ОК')}
icon={IconDatabase}
color="success"
description="Все системы в норме"
color={loading ? 'secondary' : (error ? 'danger' : 'success')}
description={error ? 'Проблемы с подключением' : 'Все API работают'}
/>
</div>
</div>