"""分享链接生成。""" from __future__ import annotations import os from pathlib import Path from urllib.parse import quote from db import list_nodes from nodes_util import hy2_port def load_env() -> dict[str, str]: root = Path(os.environ.get("JIEDIAN_ROOT", Path(__file__).resolve().parents[1])) env: dict[str, str] = {} for line in (root / ".env").read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, value = line.partition("=") env[key.strip()] = value.strip() return env def _sni_qs(value: str) -> str: """Encode dots in SNI; some clients misparsed sni=www.microsoft.com.""" return value.replace(".", "%2E") def build_links(node: dict, env: dict | None = None) -> dict[str, str | dict[str, str]]: env = env or load_env() vps_ip = env["VPS_IP"] domain = env["DOMAIN"] reality_sni = env.get("REALITY_SERVER_NAME", "www.microsoft.com") public_key = env["REALITY_PUBLIC_KEY"] short_id = env["REALITY_SHORT_ID"] name = quote(node["name"]) port = hy2_port(node, list_nodes()) # Parameter order follows Xray VLESS share-link convention; pbk/sid stay raw. vless = ( f"vless://{node['uuid']}@{vps_ip}:443" f"?type=tcp&security=reality&encryption=none&flow=xtls-rprx-vision" f"&sni={_sni_qs(reality_sni)}&fp=chrome&pbk={public_key}&sid={short_id}" f"&spx=%2F#{name}" ) hy2 = ( f"hy2://{quote(node['hy2_password'], safe='')}@{domain}:{port}" f"?sni={_sni_qs(domain)}#{name}-Hy2" ) return { "vless": vless, "hy2": hy2, "meta": { "address": vps_ip, "port": "443", "uuid": node["uuid"], "sni": reality_sni, "pbk": public_key, "sid": short_id, "spx": "/", "fp": "chrome", "flow": "xtls-rprx-vision", }, }