feat: Add new endpoint to fetch last modified dates of S3 files and update Dashboard to display this information
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 16m53s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 16m53s
This commit is contained in:
@@ -126,6 +126,23 @@ app.post('/api/asns', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Новый эндпоинт для получения дат последнего изменения файлов S3
|
||||||
|
app.get('/api/s3/last-modified', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const [domainsHead, asnsHead] = await Promise.all([
|
||||||
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'domains.txt' }).promise(),
|
||||||
|
s3.headObject({ Bucket: BUCKET_NAME, Key: 'asns.txt' }).promise()
|
||||||
|
]);
|
||||||
|
res.json({
|
||||||
|
domainsLastModified: domainsHead.LastModified ? domainsHead.LastModified.toISOString() : null,
|
||||||
|
asnsLastModified: asnsHead.LastModified ? asnsHead.LastModified.toISOString() : null
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
res.status(500).send('Error fetching last modified dates from S3');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// The "catchall" handler: for any request that doesn't
|
// The "catchall" handler: for any request that doesn't
|
||||||
// match one above, send back React's index.html file.
|
// match one above, send back React's index.html file.
|
||||||
app.get('*', (req, res) => {
|
app.get('*', (req, res) => {
|
||||||
|
|||||||
+42
-86
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Container } from 'react-bootstrap';
|
import { Container } from 'react-bootstrap';
|
||||||
import {
|
import {
|
||||||
IconBrandTabler,
|
IconBrandTabler,
|
||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
import DataManager from './DataManager';
|
import DataManager from './DataManager';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [activeTab, setActiveTab] = useState('dashboard');
|
const [activeTab, setActiveTab] = useState('dashboard');
|
||||||
@@ -171,34 +172,60 @@ function StatCard({ icon: Icon, color, value, title, subtitle }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Dashboard() {
|
function Dashboard() {
|
||||||
|
const [stats, setStats] = useState({ domainsCount: null, asnsCount: null, lastModified: null });
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchStats() {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const [domainsRes, asnsRes, s3Res] = await Promise.all([
|
||||||
|
axios.get('/api/domains'),
|
||||||
|
axios.get('/api/asns'),
|
||||||
|
axios.get('/api/s3/last-modified')
|
||||||
|
]);
|
||||||
|
setStats({
|
||||||
|
domainsCount: domainsRes.data.length,
|
||||||
|
asnsCount: asnsRes.data.length,
|
||||||
|
lastModified: s3Res.data.domainsLastModified
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setStats({ domainsCount: null, asnsCount: null, lastModified: null });
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchStats();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="row g-3 mb-4">
|
<div className="row g-3 mb-4">
|
||||||
<div className="col-md-4">
|
<div className="col-md-4">
|
||||||
<StatCard
|
<StatCard
|
||||||
icon={IconDatabase}
|
icon={IconWorld}
|
||||||
color="blue"
|
color="blue"
|
||||||
value={6}
|
value={loading ? '...' : stats.domainsCount ?? '—'}
|
||||||
title="Событий"
|
title="Доменов"
|
||||||
subtitle="Всего событий"
|
subtitle="Всего доменов"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-4">
|
<div className="col-md-4">
|
||||||
<StatCard
|
<StatCard
|
||||||
icon={IconAlertTriangle}
|
icon={IconNetwork}
|
||||||
color="red"
|
color="green"
|
||||||
value={3}
|
value={loading ? '...' : stats.asnsCount ?? '—'}
|
||||||
title="Ошибок"
|
title="AS"
|
||||||
subtitle="Критические ошибки"
|
subtitle="Всего AS"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-4">
|
<div className="col-md-4">
|
||||||
<StatCard
|
<StatCard
|
||||||
icon={IconAlertCircle}
|
icon={IconDatabase}
|
||||||
color="yellow"
|
color="orange"
|
||||||
value={0}
|
value={loading ? '...' : (stats.lastModified ? new Date(stats.lastModified).toLocaleString() : '—')}
|
||||||
title="Предупреждений"
|
title="Обновление"
|
||||||
subtitle="Системные предупреждения"
|
subtitle="Последнее изменение S3"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -210,77 +237,6 @@ function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="row row-deck">
|
|
||||||
<div className="col-sm-6 col-lg-3">
|
|
||||||
<div className="card">
|
|
||||||
<div className="card-body">
|
|
||||||
<div className="h1 mb-3">1,234</div>
|
|
||||||
<div className="d-flex align-items-center">
|
|
||||||
<IconWorld className="icon me-2" />
|
|
||||||
<span className="text-muted">Всего доменов</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-sm-6 col-lg-3">
|
|
||||||
<div className="card">
|
|
||||||
<div className="card-body">
|
|
||||||
<div className="h1 mb-3">567</div>
|
|
||||||
<div className="d-flex align-items-center">
|
|
||||||
<IconNetwork className="icon me-2" />
|
|
||||||
<span className="text-muted">Всего AS</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-sm-6 col-lg-3">
|
|
||||||
<div className="card">
|
|
||||||
<div className="card-body">
|
|
||||||
<div className="h1 mb-3">12</div>
|
|
||||||
<div className="d-flex align-items-center">
|
|
||||||
<IconCloud className="icon me-2" />
|
|
||||||
<span className="text-muted">Активные шлюзы</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-sm-6 col-lg-3">
|
|
||||||
<div className="card">
|
|
||||||
<div className="card-body">
|
|
||||||
<div className="h1 mb-3">3</div>
|
|
||||||
<div className="d-flex align-items-center">
|
|
||||||
<IconActivity className="icon me-2" />
|
|
||||||
<span className="text-muted">Последние изменения</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="card mt-4">
|
|
||||||
<div className="card-body">
|
|
||||||
<h3 className="card-title mb-3">Добро пожаловать в S3 Lists Manager</h3>
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-md-6 mb-3">
|
|
||||||
<div className="d-flex align-items-center">
|
|
||||||
<IconWorld className="icon me-2" size={32} />
|
|
||||||
<div>
|
|
||||||
<div className="fw-bold">Управление доменами</div>
|
|
||||||
<div className="text-muted">Добавляйте, редактируйте и управляйте списками доменов</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-md-6 mb-3">
|
|
||||||
<div className="d-flex align-items-center">
|
|
||||||
<IconNetwork className="icon me-2" size={32} />
|
|
||||||
<div>
|
|
||||||
<div className="fw-bold">Управление AS</div>
|
|
||||||
<div className="text-muted">Добавляйте, редактируйте и управляйте списками AS</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user