mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-04 01:17:15 +00:00
fix(frontend): improve dense QR readability (#6396)
Dense AmneziaWG configs crossed a QR version boundary at the fixed display size, and the generated symbol had no quiet zone. Use low error correction and a four-module margin to reduce module density while keeping the payload unchanged.
This commit is contained in:
@@ -140,6 +140,8 @@ export default function QrPanel({
|
|||||||
className="qr-code"
|
className="qr-code"
|
||||||
value={value}
|
value={value}
|
||||||
size={size}
|
size={size}
|
||||||
|
errorLevel="L"
|
||||||
|
marginSize={4}
|
||||||
type="svg"
|
type="svg"
|
||||||
bordered={false}
|
bordered={false}
|
||||||
color="#000000"
|
color="#000000"
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import { genAmneziaWGConfig } from '@/lib/xray/inbound-link';
|
||||||
|
import QrPanel from '@/pages/inbounds/qr/QrPanel';
|
||||||
|
import { AmneziawgInboundSettingsSchema } from '@/schemas/protocols/inbound/amneziawg';
|
||||||
|
|
||||||
|
const KEY = `${'A'.repeat(43)}=`;
|
||||||
|
|
||||||
|
function awgConfig(disableCookies: boolean): string {
|
||||||
|
const settings = AmneziawgInboundSettingsSchema.parse({
|
||||||
|
server: {
|
||||||
|
publicKey: KEY,
|
||||||
|
mtu: 1420,
|
||||||
|
primaryDns: '8.8.8.8',
|
||||||
|
secondaryDns: '8.8.4.4',
|
||||||
|
jc: 4,
|
||||||
|
jmin: 65,
|
||||||
|
jmax: 220,
|
||||||
|
s1: 87,
|
||||||
|
s2: 44,
|
||||||
|
s3: 21,
|
||||||
|
s4: 19,
|
||||||
|
h1: '462980921-463150218',
|
||||||
|
h2: '1177681572-1177787900',
|
||||||
|
h3: '1907413509-1907903969',
|
||||||
|
h4: '2029908558-2030313135',
|
||||||
|
i1: '<r 148>',
|
||||||
|
headerProtectionKey: KEY,
|
||||||
|
contentPaddingAddition: '17-49',
|
||||||
|
rekeyAfterTime: '111-139',
|
||||||
|
rekeyTimeout: '4-7',
|
||||||
|
rejectAfterTime: '187-251',
|
||||||
|
keepaliveTimeout: '9-14',
|
||||||
|
maxHandshakeAttempts: '19-36',
|
||||||
|
randomTrailers: true,
|
||||||
|
disableCookies,
|
||||||
|
},
|
||||||
|
clients: [
|
||||||
|
{
|
||||||
|
email: 'my-client',
|
||||||
|
privateKey: KEY,
|
||||||
|
preSharedKey: KEY,
|
||||||
|
allowedIPs: ['10.8.1.2/32'],
|
||||||
|
keepAlive: 25,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return genAmneziaWGConfig({
|
||||||
|
settings,
|
||||||
|
address: 'your-server.example.com',
|
||||||
|
port: 443,
|
||||||
|
remark: 'my-client',
|
||||||
|
peerIndex: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function qrGeometry(value: string): { viewBox: string; foreground: string } {
|
||||||
|
const { container } = render(<QrPanel value={value} />);
|
||||||
|
const svg = container.querySelector('.qr-code svg');
|
||||||
|
const paths = svg?.querySelectorAll('path');
|
||||||
|
|
||||||
|
expect(svg).not.toBeNull();
|
||||||
|
expect(paths).toHaveLength(2);
|
||||||
|
|
||||||
|
return {
|
||||||
|
viewBox: svg?.getAttribute('viewBox') ?? '',
|
||||||
|
foreground: paths?.item(1).getAttribute('d') ?? '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('QrPanel dense AmneziaWG config', () => {
|
||||||
|
it('keeps the complete 3.1 config readable across the DisableCookies QR boundary', () => {
|
||||||
|
const complete = awgConfig(true);
|
||||||
|
const withoutDisableCookies = awgConfig(false);
|
||||||
|
|
||||||
|
expect(complete).toContain('DisableCookies = on\n');
|
||||||
|
expect(complete.length - withoutDisableCookies.length).toBe(20);
|
||||||
|
|
||||||
|
const completeQr = qrGeometry(complete);
|
||||||
|
const shorterQr = qrGeometry(withoutDisableCookies);
|
||||||
|
|
||||||
|
expect(completeQr.viewBox).toBe('0 0 105 105');
|
||||||
|
expect(shorterQr.viewBox).toBe('0 0 101 101');
|
||||||
|
expect(completeQr.foreground).toMatch(/^M4 4h7/);
|
||||||
|
expect(shorterQr.foreground).toMatch(/^M4 4h7/);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user