CI / changes (push) Successful in 7s
CI / openapi (push) Has been skipped
CI / web (push) Successful in 29s
CI / go (push) Has been skipped
CI / docker-web (push) Successful in 1m22s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Has been skipped
CI / docker-go (push) Has been skipped
- Standardized formatting in various Svelte components for better readability. - Updated README.md and .prettierrc for consistent styling and improved documentation. - Refactored class attributes in UI components to enhance clarity and maintainability. - Adjusted layout files to ensure proper alignment and spacing in the application interface.
116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
import type { RevisionPrefix } from '$lib/api/types.js';
|
|
|
|
export function prefixStringFromItem(p: string | RevisionPrefix): string {
|
|
if (typeof p === 'string') return p;
|
|
return p.prefix ?? '';
|
|
}
|
|
|
|
function parseIPv4Octets(addr: string): [number, number, number, number] | null {
|
|
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(addr);
|
|
if (!m) return null;
|
|
const o = [m[1], m[2], m[3], m[4]].map((x) => parseInt(x, 10));
|
|
if (o.some((n) => !Number.isFinite(n) || n < 0 || n > 255)) return null;
|
|
return [o[0], o[1], o[2], o[3]];
|
|
}
|
|
|
|
/** 8 групп по 16 бит + длина префикса (или null, если не распознали). */
|
|
function parseIPv6(addrPart: string): { words: number[]; pl: number } | null {
|
|
if (!addrPart.includes(':')) return null;
|
|
const zone = addrPart.indexOf('%');
|
|
if (zone >= 0) addrPart = addrPart.slice(0, zone);
|
|
const parts = addrPart.split('::');
|
|
if (parts.length > 2) return null;
|
|
|
|
const parseGroup = (h: string) => parseInt(h, 16);
|
|
|
|
if (parts.length === 1) {
|
|
const g = parts[0].split(':').filter(Boolean);
|
|
if (g.length === 8) {
|
|
return { words: g.map(parseGroup), pl: 128 };
|
|
}
|
|
// IPv4-хвост: x:x:x:x:x:x:a.b.c.d (6 групп + IPv4)
|
|
if (g.length === 7 && g[6].includes('.')) {
|
|
const o = parseIPv4Octets(g[6]);
|
|
if (!o) return null;
|
|
const w = g.slice(0, 6).map(parseGroup);
|
|
w.push((o[0] << 8) | o[1], (o[2] << 8) | o[3]);
|
|
return { words: w, pl: 128 };
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const left = parts[0] ? parts[0].split(':').filter(Boolean) : [];
|
|
const right = parts[1] ? parts[1].split(':').filter(Boolean) : [];
|
|
const lastR = right[right.length - 1];
|
|
if (lastR && lastR.includes('.')) {
|
|
const o = parseIPv4Octets(lastR);
|
|
if (!o) return null;
|
|
const rWords = right.slice(0, -1).map(parseGroup);
|
|
rWords.push((o[0] << 8) | o[1], (o[2] << 8) | o[3]);
|
|
const missing = 8 - left.length - rWords.length;
|
|
if (missing < 0) return null;
|
|
return { words: [...left.map(parseGroup), ...Array(missing).fill(0), ...rWords], pl: 128 };
|
|
}
|
|
const missing = 8 - left.length - right.length;
|
|
if (missing < 0) return null;
|
|
return {
|
|
words: [...left.map(parseGroup), ...Array(missing).fill(0), ...right.map(parseGroup)],
|
|
pl: 128
|
|
};
|
|
}
|
|
|
|
type SortKey =
|
|
| { t: 0; v4: [number, number, number, number]; pl: number }
|
|
| { t: 1; v6: number[]; pl: number }
|
|
| { t: 2; s: string };
|
|
|
|
function sortKeyForCidr(s: string): SortKey {
|
|
const slash = s.indexOf('/');
|
|
const addr = slash >= 0 ? s.slice(0, slash) : s;
|
|
const plStr = slash >= 0 ? s.slice(slash + 1) : '';
|
|
const plNum = plStr ? parseInt(plStr, 10) : NaN;
|
|
|
|
const v4 = parseIPv4Octets(addr);
|
|
if (v4 && !addr.includes(':')) {
|
|
const pl = Number.isFinite(plNum) ? plNum : 32;
|
|
return { t: 0, v4, pl };
|
|
}
|
|
|
|
const v6 = parseIPv6(addr);
|
|
if (v6) {
|
|
const pl = Number.isFinite(plNum) ? plNum : v6.pl;
|
|
return { t: 1, v6: v6.words, pl };
|
|
}
|
|
|
|
return { t: 2, s: s.toLowerCase() };
|
|
}
|
|
|
|
function compareKeys(a: SortKey, b: SortKey): number {
|
|
if (a.t !== b.t) return a.t - b.t;
|
|
if (a.t === 2 && b.t === 2) {
|
|
return a.s.localeCompare(b.s, 'en', { numeric: true, sensitivity: 'base' });
|
|
}
|
|
if (a.t === 0 && b.t === 0) {
|
|
for (let i = 0; i < 4; i++) {
|
|
if (a.v4[i] !== b.v4[i]) return a.v4[i] - b.v4[i];
|
|
}
|
|
return a.pl - b.pl;
|
|
}
|
|
if (a.t === 1 && b.t === 1) {
|
|
for (let i = 0; i < 8; i++) {
|
|
if (a.v6[i] !== b.v6[i]) return a.v6[i] - b.v6[i];
|
|
}
|
|
return a.pl - b.pl;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** Сортировка CIDR и прочих строк префиксов (as:, домены и т.д.) для отображения. */
|
|
export function sortPrefixStrings(strings: string[]): string[] {
|
|
return [...strings].sort((a, b) => compareKeys(sortKeyForCidr(a), sortKeyForCidr(b)));
|
|
}
|
|
|
|
export function sortRevisionDiffItems(items: (string | RevisionPrefix)[]): string[] {
|
|
return sortPrefixStrings(items.map(prefixStringFromItem));
|
|
}
|