feat: Добавить улучшенный rate limiting для операций записи и BGP обновлений, а также валидацию входных данных для ASNs, доменов и IP диапазонов. Реализовать отображение статистики использования сообществ и новый API для валидации конфигурации MikroTik. Обновить интерфейс менеджера сообществ с вкладками для списка и статистики.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 3m30s

This commit is contained in:
2025-10-02 16:36:52 +07:00
parent c06945eefc
commit cfbcedf9d7
8 changed files with 1501 additions and 17 deletions
+41 -3
View File
@@ -13,13 +13,16 @@ import {
IconDownload,
IconDeviceFloppy,
IconHash,
IconFilter
IconFilter,
IconChartBar,
IconList
} from '@tabler/icons-react';
import PageHeaderActions from './components/PageHeaderActions.jsx';
import PageHeader from './components/PageHeader.jsx';
import EmptyState from './components/EmptyState.jsx';
import { TableEmpty } from './components/TableSkeleton.jsx';
import Breadcrumbs from './components/Breadcrumbs.jsx';
import CommunityStats from './components/CommunityStats.jsx';
const API_URL = '/api';
@@ -33,10 +36,13 @@ function CommunitiesManager() {
const [sortOrder, setSortOrder] = useState('asc');
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 10;
// Состояние для переключения между списком и статистикой
const [activeTab, setActiveTab] = useState('list'); // 'list' | 'stats'
const [newItem, setNewItem] = useState({ value: '', name: '', description: '', tags: '', gatewayDefault: '', color: '' });
const [newItem, setNewItem] = useState({ value: '', name: '', description: '', tags: '', gatewayDefault: '', color: '', category: '', priority: 0, enabled: true });
const [editingValue, setEditingValue] = useState(null);
const [editingDraft, setEditingDraft] = useState({ value: '', name: '', description: '', tags: '', gatewayDefault: '', color: '' });
const [editingDraft, setEditingDraft] = useState({ value: '', name: '', description: '', tags: '', gatewayDefault: '', color: '', category: '', priority: 0, enabled: true });
const editRef = useRef(null);
useEffect(() => { fetchItems(); }, []);
@@ -251,6 +257,30 @@ function CommunitiesManager() {
)}
/>
{/* Вкладки: Список / Статистика */}
<div className="card mb-3">
<div className="card-body p-2">
<div className="btn-group w-100" role="group">
<button
type="button"
className={`btn ${activeTab === 'list' ? 'btn-primary' : 'btn-outline-primary'}`}
onClick={() => setActiveTab('list')}
>
<IconList className="me-2" size={18} />
Список communities
</button>
<button
type="button"
className={`btn ${activeTab === 'stats' ? 'btn-primary' : 'btn-outline-primary'}`}
onClick={() => setActiveTab('stats')}
>
<IconChartBar className="me-2" size={18} />
Статистика использования
</button>
</div>
</div>
</div>
{error && (
<div className="alert alert-danger alert-dismissible" role="alert">
<IconX className="me-2" />{error}
@@ -264,6 +294,8 @@ function CommunitiesManager() {
</div>
)}
{/* Отображение списка communities */}
{activeTab === 'list' && (
<div className="row g-3">
<div className="col-lg-3">
<div className="card card-md">
@@ -408,6 +440,12 @@ function CommunitiesManager() {
</div>
</div>
</div>
)}
{/* Отображение статистики communities */}
{activeTab === 'stats' && (
<CommunityStats communities={items} />
)}
</div>
);
}