feat: Добавить опцию агрессивной агрегации для IP-диапазонов, позволяющую сворачивать более 2 префиксов в одном /16; обновить интерфейс для управления этой настройкой
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m52s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m52s
This commit is contained in:
@@ -228,7 +228,7 @@ function IPRangesManager() {
|
||||
const [analysisResultsOpen, setAnalysisResultsOpen] = useState(false);
|
||||
const [analysisPreview, setAnalysisPreview] = useState(null);
|
||||
const [overwriteConfirmOpen, setOverwriteConfirmOpen] = useState(false);
|
||||
const [analyzeFilters, setAnalyzeFilters] = useState({ community: '', minMask: 0, maxMask: 32, type: 'any' });
|
||||
const [analyzeFilters, setAnalyzeFilters] = useState({ community: '', minMask: 0, maxMask: 32, type: 'any', supernet16: false });
|
||||
|
||||
const handlePreviewDiff = () => {
|
||||
const valid = items.filter(i => isValidCidr(i.ipRange) && isValidCommunity(i.community))
|
||||
@@ -437,6 +437,7 @@ function IPRangesManager() {
|
||||
parts.push(`Маска: /${analyzeFilters.minMask}..../${analyzeFilters.maxMask}`);
|
||||
const typeLabel = analyzeFilters.type === 'any' ? 'любой' : (analyzeFilters.type === 'private' ? 'только приватные' : (analyzeFilters.type === 'public' ? 'только публичные' : 'только CGNAT'));
|
||||
parts.push(`Тип: ${typeLabel}`);
|
||||
parts.push(`Агрегировать >2 префиксов в одном /16 до /16: ${analyzeFilters.supernet16 ? 'да' : 'нет'}`);
|
||||
return parts.join('\n');
|
||||
};
|
||||
|
||||
@@ -522,6 +523,28 @@ function IPRangesManager() {
|
||||
return out;
|
||||
};
|
||||
|
||||
// Агрессивная агрегация: если в одном /16 > 2 префиксов — свернуть в /16
|
||||
const get16BaseCidr = (cidr) => {
|
||||
const ip = String(cidr).split('/')[0];
|
||||
const ipNum = ipToInt(ip);
|
||||
const start = ipNum & (~0n << 16n);
|
||||
return `${intToIp(start)}/16`;
|
||||
};
|
||||
|
||||
const applySupernet16 = (cidrs) => {
|
||||
const buckets = new Map();
|
||||
for (const c of cidrs) {
|
||||
const base16 = get16BaseCidr(c);
|
||||
if (!buckets.has(base16)) buckets.set(base16, []);
|
||||
buckets.get(base16).push(c);
|
||||
}
|
||||
const out = [];
|
||||
for (const [base16, list] of buckets) {
|
||||
if (list.length > 2) out.push(base16); else out.push(...list);
|
||||
}
|
||||
return Array.from(new Set(out));
|
||||
};
|
||||
|
||||
const runAnalysis = () => {
|
||||
const community = String(analyzeFilters.community || '').trim();
|
||||
if (!community) { window.notify?.error?.('Выберите community'); return; }
|
||||
@@ -539,7 +562,10 @@ function IPRangesManager() {
|
||||
const unchanged = allForCommunity.filter(i => !filtered.includes(i));
|
||||
|
||||
const beforeCidrs = filtered.map(i => i.ipRange);
|
||||
const afterCidrs = summarizeCidrs(beforeCidrs);
|
||||
let afterCidrs = summarizeCidrs(beforeCidrs);
|
||||
if (analyzeFilters.supernet16) {
|
||||
afterCidrs = applySupernet16(afterCidrs);
|
||||
}
|
||||
const processedBefore = beforeCidrs.length;
|
||||
const processedAfter = afterCidrs.length;
|
||||
const totalAfter = processedAfter + unchanged.length;
|
||||
@@ -1018,6 +1044,14 @@ function IPRangesManager() {
|
||||
</div>
|
||||
<div className="form-text">Определение типа по первому IP в CIDR.</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Агрессивная агрегация</label>
|
||||
<label className="form-check">
|
||||
<input className="form-check-input" type="checkbox" checked={analyzeFilters.supernet16} onChange={e => setAnalyzeFilters(f => ({ ...f, supernet16: e.target.checked }))} />
|
||||
<span className="form-check-label">Если в одном /16 более 2 префиксов — свернуть в /16</span>
|
||||
</label>
|
||||
<div className="form-text">Например: 157.240.12.0/23, 157.240.15.0/24, 157.240.16.0/23 → 157.240.0.0/16</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user