mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-20 19:01:17 +00:00
fix(ui): format host-cidr and satisfy strict typecheck rules
This commit is contained in:
parent
265238f630
commit
61c02871d2
4 changed files with 20 additions and 119 deletions
|
|
@ -56,11 +56,7 @@ export function parseIPv6(ipStr: string): bigint | null {
|
|||
}
|
||||
|
||||
const missingZeros = 8 - totalParts;
|
||||
const allParts = [
|
||||
...leftParts,
|
||||
...Array(missingZeros).fill('0'),
|
||||
...rightParts,
|
||||
];
|
||||
const allParts = [...leftParts, ...Array(missingZeros).fill('0'), ...rightParts];
|
||||
|
||||
let result = 0n;
|
||||
for (const part of allParts) {
|
||||
|
|
@ -104,8 +100,14 @@ export function formatIPv6(val: bigint): string {
|
|||
}
|
||||
|
||||
if (bestLen >= 2) {
|
||||
const left = parts.slice(0, bestStart).map((p) => p.toString(16)).join(':');
|
||||
const right = parts.slice(bestStart + bestLen).map((p) => p.toString(16)).join(':');
|
||||
const left = parts
|
||||
.slice(0, bestStart)
|
||||
.map((p) => p.toString(16))
|
||||
.join(':');
|
||||
const right = parts
|
||||
.slice(bestStart + bestLen)
|
||||
.map((p) => p.toString(16))
|
||||
.join(':');
|
||||
return `${left}::${right}`;
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +159,7 @@ export function randomIPsFromCIDR(cidrStr: string, count: number): string[] {
|
|||
|
||||
function randomIPv4FromCIDR(ip4: number, prefix: number, count: number): string[] {
|
||||
const hostBits = 32 - prefix;
|
||||
const mask = prefix === 0 ? 0 : ((~0 << hostBits) >>> 0);
|
||||
const mask = prefix === 0 ? 0 : (~0 << hostBits) >>> 0;
|
||||
const baseU32 = (ip4 & mask) >>> 0;
|
||||
|
||||
if (hostBits === 0) {
|
||||
|
|
@ -199,7 +201,7 @@ function randomIPv4FromCIDR(ip4: number, prefix: number, count: number): string[
|
|||
|
||||
function randomIPv6FromCIDR(ip6: bigint, prefix: number, count: number): string[] {
|
||||
const hostBits = 128 - prefix;
|
||||
const mask = prefix === 0 ? 0n : (((1n << 128n) - 1n) ^ ((1n << BigInt(hostBits)) - 1n));
|
||||
const mask = prefix === 0 ? 0n : ((1n << 128n) - 1n) ^ ((1n << BigInt(hostBits)) - 1n);
|
||||
const baseInt = ip6 & mask;
|
||||
|
||||
if (hostBits === 0) {
|
||||
|
|
|
|||
|
|
@ -121,13 +121,11 @@ export function hostEndpointsFor(
|
|||
const ips = randomIPsFromCIDR(dest, count);
|
||||
if (ips.length > 0) {
|
||||
for (let i = 0; i < ips.length; i++) {
|
||||
const ip = ips[i];
|
||||
const remark =
|
||||
ips.length > 1
|
||||
? record.remark
|
||||
? `${record.remark} - ${i + 1}`
|
||||
: `${i + 1}`
|
||||
: record.remark || '';
|
||||
const ip = ips[i]!;
|
||||
let remark = record.remark ?? '';
|
||||
if (ips.length > 1) {
|
||||
remark = record.remark ? `${record.remark} - ${i + 1}` : `${i + 1}`;
|
||||
}
|
||||
const endpoint: HostEndpoint = { dest: ip, port, remark };
|
||||
const sni = record.overrideSniFromAddress ? ip : record.sni;
|
||||
if (!record.keepSniBlank && sni) endpoint.sni = sni;
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
cleanCIDR,
|
||||
formatIPv4,
|
||||
formatIPv6,
|
||||
isCIDR,
|
||||
MAX_RANDOM_CIDR_COUNT,
|
||||
parseIPv4,
|
||||
parseIPv6,
|
||||
randomIPsFromCIDR,
|
||||
} from '@/lib/hosts/host-cidr';
|
||||
|
||||
describe('cleanCIDR', () => {
|
||||
it('handles standard and bracketed forms', () => {
|
||||
expect(cleanCIDR('192.168.1.0/24')).toBe('192.168.1.0/24');
|
||||
expect(cleanCIDR('[192.168.1.0/24]')).toBe('192.168.1.0/24');
|
||||
expect(cleanCIDR('2001:db8::/64')).toBe('2001:db8::/64');
|
||||
expect(cleanCIDR('[2001:db8::/64]')).toBe('2001:db8::/64');
|
||||
expect(cleanCIDR('[2001:db8::]/64')).toBe('2001:db8::/64');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isCIDR', () => {
|
||||
it('validates IPv4 CIDR strings', () => {
|
||||
expect(isCIDR('192.168.1.0/24')).toBe(true);
|
||||
expect(isCIDR('10.0.0.0/8')).toBe(true);
|
||||
expect(isCIDR('0.0.0.0/0')).toBe(true);
|
||||
expect(isCIDR('192.168.1.1/32')).toBe(true);
|
||||
expect(isCIDR('192.168.1.1/33')).toBe(false);
|
||||
expect(isCIDR('192.168.1.1')).toBe(false);
|
||||
});
|
||||
|
||||
it('validates IPv6 CIDR strings including bracketed forms', () => {
|
||||
expect(isCIDR('2001:db8::/64')).toBe(true);
|
||||
expect(isCIDR('[2001:db8::]/64')).toBe(true);
|
||||
expect(isCIDR('[2001:db8::/64]')).toBe(true);
|
||||
expect(isCIDR('::/0')).toBe(true);
|
||||
expect(isCIDR('fe80::/10')).toBe(true);
|
||||
expect(isCIDR('2001:db8::1')).toBe(false);
|
||||
expect(isCIDR('example.com')).toBe(false);
|
||||
expect(isCIDR('')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('randomIPsFromCIDR', () => {
|
||||
it('generates random usable IPs for IPv4 CIDR', () => {
|
||||
const ips = randomIPsFromCIDR('192.168.1.0/24', 5);
|
||||
expect(ips).toHaveLength(5);
|
||||
const unique = new Set(ips);
|
||||
expect(unique.size).toBe(5);
|
||||
for (const ip of ips) {
|
||||
expect(ip.startsWith('192.168.1.')).toBe(true);
|
||||
expect(ip).not.toBe('192.168.1.0');
|
||||
expect(ip).not.toBe('192.168.1.255');
|
||||
}
|
||||
});
|
||||
|
||||
it('generates random usable IPs for bracketed IPv6 CIDR', () => {
|
||||
const ips = randomIPsFromCIDR('[2001:db8::]/64', 3);
|
||||
expect(ips).toHaveLength(3);
|
||||
const unique = new Set(ips);
|
||||
expect(unique.size).toBe(3);
|
||||
for (const ip of ips) {
|
||||
expect(ip.startsWith('2001:db8:')).toBe(true);
|
||||
expect(ip).not.toBe('2001:db8::');
|
||||
}
|
||||
});
|
||||
|
||||
it('caps count at MAX_RANDOM_CIDR_COUNT', () => {
|
||||
const ips = randomIPsFromCIDR('2001:db8::/64', 500);
|
||||
expect(ips).toHaveLength(MAX_RANDOM_CIDR_COUNT);
|
||||
});
|
||||
|
||||
it('respects small subnet bounds', () => {
|
||||
const ips = randomIPsFromCIDR('192.0.2.0/30', 10);
|
||||
expect(ips).toHaveLength(2);
|
||||
expect(ips.sort()).toEqual(['192.0.2.1', '192.0.2.2']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('IP parsers and formatters', () => {
|
||||
it('handles IPv4 roundtrip', () => {
|
||||
const u32 = parseIPv4('10.20.30.40');
|
||||
expect(u32).not.toBeNull();
|
||||
expect(formatIPv4(u32!)).toBe('10.20.30.40');
|
||||
});
|
||||
|
||||
it('handles IPv6 roundtrip and compression', () => {
|
||||
const v6 = parseIPv6('2001:db8::1');
|
||||
expect(v6).not.toBeNull();
|
||||
expect(formatIPv6(v6!)).toBe('2001:db8::1');
|
||||
|
||||
const v6Zero = parseIPv6('::');
|
||||
expect(v6Zero).toBe(0n);
|
||||
expect(formatIPv6(v6Zero!)).toBe('::');
|
||||
});
|
||||
});
|
||||
|
|
@ -183,12 +183,11 @@ describe('clients page tunnel configs follow Hosts', () => {
|
|||
];
|
||||
const eps = tunnelConfigEndpoints(inbound, cidrHosts, PANEL, '');
|
||||
expect(eps).toHaveLength(2);
|
||||
expect(eps[0].remark).toBe('CIDR-WG - 1');
|
||||
expect(eps[1].remark).toBe('CIDR-WG - 2');
|
||||
expect(eps.map((e) => e.port)).toEqual([51820, 51820]);
|
||||
expect(eps[0]?.remark).toBe('CIDR-WG - 1');
|
||||
expect(eps[1]?.remark).toBe('CIDR-WG - 2');
|
||||
expect(eps.map((e) => e?.port)).toEqual([51820, 51820]);
|
||||
for (const ep of eps) {
|
||||
expect(ep.dest).toMatch(/^198\.51\.100\.[12]$/);
|
||||
expect(ep?.dest).toMatch(/^198\.51\.100\.[12]$/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue