link-label.test.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { describe, it, expect } from 'vitest';
  2. import { parseLinkParts, linkMetaText } from '@/lib/xray/link-label';
  3. import { genAmneziaWGLink } from '@/lib/xray/inbound-link';
  4. import type { AmneziawgInboundSettings } from '@/schemas/protocols/inbound/amneziawg';
  5. // The panel shows the subscription's remark verbatim. Per-client traffic/expiry
  6. // info is rendered only into the body a client app imports (backend, first link
  7. // only), so the panel's display links are already clean — nothing is stripped.
  8. describe('link-label parseLinkParts', () => {
  9. const linkWith = (remark: string) =>
  10. `vless://[email protected]:443?type=tcp&security=tls#${encodeURIComponent(remark)}`;
  11. it('parses protocol / network / security and keeps the remark verbatim', () => {
  12. const parts = parseLinkParts(linkWith('[email protected]'));
  13. expect(parts?.protocol).toBe('Vless');
  14. expect(parts?.network).toBe('TCP');
  15. expect(parts?.security).toBe('TLS');
  16. expect(parts?.remark).toBe('[email protected]');
  17. expect(parts?.port).toBe('443');
  18. });
  19. it('linkMetaText joins the remark with the port', () => {
  20. const parts = parseLinkParts(linkWith('[email protected]'));
  21. expect(parts && linkMetaText(parts)).toBe('[email protected]:443');
  22. });
  23. it('returns null for an unparseable scheme', () => {
  24. expect(parseLinkParts('not-a-link')).toBeNull();
  25. });
  26. // MTProto share links are tg://proxy deep links whose port rides in a query
  27. // param, not the URL authority; they carry no transport and use FakeTLS.
  28. it('labels an mtproto tg://proxy link with its query-param port and FakeTLS', () => {
  29. const parts = parseLinkParts(
  30. 'tg://proxy?server=host.example.com&port=8443&secret=ee00#mt-inbound',
  31. );
  32. expect(parts?.protocol).toBe('MTProto');
  33. expect(parts?.network).toBe('');
  34. expect(parts?.security).toBe('FAKETLS');
  35. expect(parts?.port).toBe('8443');
  36. expect(parts?.remark).toBe('mt-inbound');
  37. expect(parts && linkMetaText(parts)).toBe('mt-inbound:8443');
  38. });
  39. // AmneziaWG's vpn:// links are base64url of a plain .conf text, not a
  40. // structured URL (see inbound-link.ts's genAmneziaWGLink) -- there's no
  41. // query string or #hash available, so the remark/port have to be read back
  42. // out of the decoded .conf body instead. Regression test for a real report:
  43. // these links were showing a generic "Vpn" tag and falling back to "Link N"
  44. // instead of "AmneziaWG" + the actual remark:port, unlike every other
  45. // protocol's link row.
  46. it('labels an AmneziaWG vpn:// link with its decoded remark and endpoint port', () => {
  47. const settings = {
  48. server: {
  49. publicKey: 'serverPubKey==',
  50. jc: 5,
  51. jmin: 10,
  52. jmax: 50,
  53. s1: 30,
  54. s2: 45,
  55. s3: 10,
  56. s4: 5,
  57. h1: '',
  58. h2: '',
  59. h3: '',
  60. h4: '',
  61. i1: '',
  62. },
  63. clients: [{ email: 'peer-1', privateKey: 'clientPrivKey==', allowedIPs: ['10.8.1.2/32'] }],
  64. } as unknown as AmneziawgInboundSettings;
  65. // Cyrillic remark on purpose -- matches the real report, and exercises
  66. // the unicode round-trip through base64url (not just plain ASCII).
  67. const link = genAmneziaWGLink({
  68. settings,
  69. address: 'awg.example.test',
  70. port: 36541,
  71. remark: 'wg-Майфун',
  72. peerIndex: 0,
  73. });
  74. const parts = parseLinkParts(link);
  75. expect(parts?.protocol).toBe('AmneziaWG');
  76. expect(parts?.remark).toBe('wg-Майфун');
  77. expect(parts?.port).toBe('36541');
  78. expect(parts && linkMetaText(parts)).toBe('wg-Майфун:36541');
  79. });
  80. it('labels a tuic link with QUIC network and TLS security', () => {
  81. const link = 'tuic://uuid:[email protected]:8443?congestion_control=bbr#tuic-remark';
  82. const parts = parseLinkParts(link);
  83. expect(parts?.protocol).toBe('TUIC');
  84. expect(parts?.network).toBe('QUIC');
  85. expect(parts?.security).toBe('TLS');
  86. expect(parts?.port).toBe('8443');
  87. expect(parts?.remark).toBe('tuic-remark');
  88. });
  89. });