mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-08 21:56:08 +00:00
9b91f0f42e
Fold the standalone 3x-ui-docs project (Next.js 16 + Fumadocs, deployed to docs.sanaei.dev) into docs/ so the panel and its documentation share a single source of truth, the way sing-box keeps its docs in-tree. The old repo becomes redundant and can be retired. - Import the full site under docs/ (app, components, content, lib, public, scripts, config). The self-contained pnpm project sits alongside the existing engineering notes with no filename collisions. - Re-point "Edit on GitHub" links from MHSanaei/3x-ui-docs to this repo's docs/content/docs path (docs/lib/shared.ts, docs/app/.../page.tsx). - Add docs-ci.yml and docs-deploy.yml under .github/workflows/, scoped to docs/** and run with working-directory: docs, since GitHub only runs workflows from the repo-root .github/. deploy-static.yml's GitHub Pages publish (CNAME docs.sanaei.dev) carries over unchanged. Follow-up (outside this commit): attach the docs.sanaei.dev custom domain to this repository's Pages (or set the Vercel project's root directory to docs), confirm the site is live from the monorepo, then delete MHSanaei/3x-ui-docs.
84 lines
2.8 KiB
TypeScript
84 lines
2.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
generateX25519KeyPair,
|
|
isX25519Available,
|
|
randomShortId,
|
|
realityClientLink,
|
|
realityServerInbound,
|
|
type RealityConfig,
|
|
} from './reality';
|
|
import { base64UrlToBytes } from './base64';
|
|
import { parseLink } from './links';
|
|
|
|
describe('X25519 keygen', () => {
|
|
it('is available in this environment (Node 22+ / modern browser)', () => {
|
|
expect(isX25519Available()).toBe(true);
|
|
});
|
|
|
|
it('produces 32-byte base64url keys that differ', async () => {
|
|
const { privateKey, publicKey } = await generateX25519KeyPair();
|
|
expect(privateKey).not.toMatch(/[+/=]/);
|
|
expect(publicKey).not.toMatch(/[+/=]/);
|
|
expect(base64UrlToBytes(privateKey)).toHaveLength(32);
|
|
expect(base64UrlToBytes(publicKey)).toHaveLength(32);
|
|
expect(privateKey).not.toEqual(publicKey);
|
|
});
|
|
|
|
it('generates a fresh keypair each call', async () => {
|
|
const a = await generateX25519KeyPair();
|
|
const b = await generateX25519KeyPair();
|
|
expect(a.privateKey).not.toEqual(b.privateKey);
|
|
});
|
|
});
|
|
|
|
describe('randomShortId', () => {
|
|
it('returns lowercase hex of the requested byte length', () => {
|
|
const id = randomShortId(4);
|
|
expect(id).toMatch(/^[0-9a-f]{8}$/);
|
|
});
|
|
});
|
|
|
|
const CONFIG: RealityConfig = {
|
|
address: 'example.com',
|
|
port: 443,
|
|
uuid: '11111111-2222-3333-4444-555555555555',
|
|
dest: 'www.microsoft.com:443',
|
|
serverNames: ['www.microsoft.com'],
|
|
shortIds: ['ab12'],
|
|
privateKey: 'PRIV',
|
|
publicKey: 'PUB',
|
|
fingerprint: 'chrome',
|
|
spiderX: '/',
|
|
flow: 'xtls-rprx-vision',
|
|
};
|
|
|
|
describe('realityClientLink', () => {
|
|
it('builds a parseable vless link with the public REALITY params', () => {
|
|
const link = realityClientLink(CONFIG);
|
|
const parsed = parseLink(link);
|
|
expect(parsed.protocol).toBe('vless');
|
|
expect(parsed.credential).toBe(CONFIG.uuid);
|
|
expect(parsed.port).toBe(443);
|
|
expect(parsed.params.security).toBe('reality');
|
|
expect(parsed.params.pbk).toBe('PUB');
|
|
expect(parsed.params.sid).toBe('ab12');
|
|
expect(parsed.params.sni).toBe('www.microsoft.com');
|
|
expect(parsed.params.flow).toBe('xtls-rprx-vision');
|
|
// The private key must never appear in a client link.
|
|
expect(link).not.toContain('PRIV');
|
|
});
|
|
});
|
|
|
|
describe('realityServerInbound', () => {
|
|
it('produces a vless+reality inbound with the private key', () => {
|
|
const inbound = realityServerInbound(CONFIG) as {
|
|
protocol: string;
|
|
streamSettings: { security: string; realitySettings: { privateKey: string; dest: string } };
|
|
};
|
|
expect(inbound.protocol).toBe('vless');
|
|
expect(inbound.streamSettings.security).toBe('reality');
|
|
expect(inbound.streamSettings.realitySettings.privateKey).toBe('PRIV');
|
|
expect(inbound.streamSettings.realitySettings.dest).toBe('www.microsoft.com:443');
|
|
});
|
|
});
|