hiddifypanel 10.85.0b20__py3-none-any.whl → 10.86.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/hutils/network/auto_ip_selector.py +19 -10
  4. hiddifypanel/hutils/network/net.py +25 -5
  5. hiddifypanel/hutils/proxy/clash.py +1 -1
  6. hiddifypanel/hutils/proxy/shared.py +118 -56
  7. hiddifypanel/hutils/proxy/xray.py +55 -40
  8. hiddifypanel/hutils/proxy/xrayjson.py +48 -28
  9. hiddifypanel/models/config.py +1 -1
  10. hiddifypanel/models/config_enum.py +4 -0
  11. hiddifypanel/models/domain.py +33 -8
  12. hiddifypanel/models/proxy.py +6 -2
  13. hiddifypanel/panel/admin/DomainAdmin.py +22 -25
  14. hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +13 -4
  15. hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +1 -1
  16. hiddifypanel/panel/custom_widgets.py +45 -0
  17. hiddifypanel/panel/init_db.py +102 -184
  18. hiddifypanel/panel/user/user.py +12 -12
  19. hiddifypanel/static/js/custom-rtl.js +1 -1
  20. hiddifypanel/templates/fake.html +11 -2
  21. hiddifypanel/templates/flaskadmin-layout.html +11 -9
  22. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  23. hiddifypanel/translations/en/LC_MESSAGES/messages.po +33 -0
  24. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  25. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +33 -0
  26. hiddifypanel/translations/my/LC_MESSAGES/messages.mo +0 -0
  27. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  28. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +33 -0
  29. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  30. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +33 -0
  31. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  32. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +33 -0
  33. hiddifypanel/translations.i18n/en.json +19 -0
  34. hiddifypanel/translations.i18n/fa.json +19 -0
  35. hiddifypanel/translations.i18n/pt.json +19 -0
  36. hiddifypanel/translations.i18n/ru.json +19 -0
  37. hiddifypanel/translations.i18n/zh.json +19 -0
  38. {hiddifypanel-10.85.0b20.dist-info → hiddifypanel-10.86.0b1.dist-info}/METADATA +1 -1
  39. {hiddifypanel-10.85.0b20.dist-info → hiddifypanel-10.86.0b1.dist-info}/RECORD +43 -43
  40. {hiddifypanel-10.85.0b20.dist-info → hiddifypanel-10.86.0b1.dist-info}/WHEEL +0 -0
  41. {hiddifypanel-10.85.0b20.dist-info → hiddifypanel-10.86.0b1.dist-info}/entry_points.txt +0 -0
  42. {hiddifypanel-10.85.0b20.dist-info → hiddifypanel-10.86.0b1.dist-info}/licenses/LICENSE.md +0 -0
  43. {hiddifypanel-10.85.0b20.dist-info → hiddifypanel-10.86.0b1.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION CHANGED
@@ -1 +1 @@
1
- 10.85.0b20
1
+ 10.86.0b1
hiddifypanel/VERSION.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # import importlib.metadata
2
2
  from datetime import datetime
3
3
 
4
- __version__ = '10.85.0b20'
5
- __release_time__= datetime.strptime('2025-07-03T10:58:47','%Y-%m-%dT%H:%M:%S')
4
+ __version__ = '10.86.0b1'
5
+ __release_time__= datetime.strptime('2025-07-06T19:30:06','%Y-%m-%dT%H:%M:%S')
6
6
  is_released_version=True
@@ -38,10 +38,10 @@ apt.ircf.space APT
38
38
  """
39
39
 
40
40
  try:
41
-
41
+
42
42
  IPASN = maxminddb.open_database('GeoLite2-ASN.mmdb')
43
43
  IPCOUNTRY = maxminddb.open_database('GeoLite2-Country.mmdb')
44
- # __ipcity = maxminddb.open_database('GeoLite2-City.mmdb')
44
+ # __ipcity = maxminddb.open_database('GeoLite2-City.mmdb')
45
45
  except BaseException as e:
46
46
  logger.error("Error can not load maxminddb")
47
47
  IPASN = {}
@@ -163,12 +163,23 @@ def __get_host_base_on_asn(ips: Union[str, List[str]], asn_short: str) -> str:
163
163
  return selected
164
164
 
165
165
 
166
- def get_clean_ip(ips: Union[str, List[str]], resolve: bool = False, default_asn: str = '') -> str:
167
- if not ips:
168
- ips = DEFAULT_IPs
169
-
170
- ips = re.split('[ \t\r\n;,]+', ips.strip())
166
+ def get_clean_ip(ips: Union[str, List[str]], resolve: bool = False) -> str:
171
167
  user_ip = get_real_user_ip()
168
+ default_asn = request.args.get("asn", '')
169
+ return get_clean_ip_user(user_ip, ips, default_asn)
170
+
171
+
172
+ split_pattern = re.compile(r'[ \t\r\n;,]+')
173
+
174
+
175
+ @cache.cache(300)
176
+ def get_clean_ip_user(user_ip, ipliststr: str, default_asn: str = '') -> tuple[str, str]:
177
+ ipliststr = ipliststr.strip()
178
+ if not ipliststr:
179
+ ipliststr = DEFAULT_IPs.strip()
180
+
181
+ ips = split_pattern.split(ipliststr)
182
+
172
183
  asn_short = get_asn_short_name(user_ip)
173
184
  country = get_country(user_ip)
174
185
  # print("Real user ip",get_real_user_ip_debug(), user_ip,asn_short,country)
@@ -181,6 +192,4 @@ def get_clean_ip(ips: Union[str, List[str]], resolve: bool = False, default_asn:
181
192
  else:
182
193
  selected_server = random.sample(ips, 1)[0]
183
194
  # print("selected_server",selected_server)
184
- if resolve:
185
- selected_server = hutils.network.get_domain_ip(selected_server) or selected_server
186
- return str(selected_server)
195
+ return str(selected_server), asn_short
@@ -20,7 +20,7 @@ from hiddifypanel.models import *
20
20
  from hiddifypanel.cache import cache
21
21
 
22
22
 
23
- def get_domain_ip(domain: str, retry: int = 3, version: Literal[4, 6] | None = None) -> Union[ipaddress.IPv4Address, ipaddress.IPv6Address, None]:
23
+ def get_domain_ip_old(domain: str, retry: int = 3, version: Literal[4, 6] | None = None) -> Union[ipaddress.IPv4Address, ipaddress.IPv6Address, None]:
24
24
  res = None
25
25
  if not version:
26
26
  try:
@@ -41,14 +41,28 @@ def get_domain_ip(domain: str, retry: int = 3, version: Literal[4, 6] | None = N
41
41
  except BaseException:
42
42
  pass
43
43
 
44
- if retry <= 0:
45
- return None
46
- if not res:
47
- return get_domain_ip(domain, retry=retry - 1, version=version)
44
+ if retry > 0:
45
+ return get_domain_ip_old(domain, retry=retry - 1, version=version)
48
46
 
47
+ if not res:
48
+ return None
49
49
  return ipaddress.ip_address(res)
50
50
 
51
51
 
52
+ def get_domain_ip(domain: str, retry: int = 3, version: Literal[4, 6] | None = None) -> Union[ipaddress.IPv4Address, ipaddress.IPv6Address, None]:
53
+ ips=get_domain_ips_cached(domain)
54
+ ips=[ip for ip in ips if version==None or (version==4 and isinstance(ip,ipaddress.IPv4Address)) or (version==6 and isinstance(ip,ipaddress.IPv6Address)) ]
55
+ if ips:
56
+ return random.sample(ips,1)[0]
57
+ return get_domain_ip_old(domain,0)
58
+
59
+ @cache.cache(300)
60
+ def get_domain_ips_cached(domain: str, retry: int = 3) -> Set[Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]:
61
+ try:
62
+ return set(ipaddress.ip_address(domain))
63
+ except:
64
+ return get_domain_ips(domain,retry)
65
+
52
66
  def get_domain_ips(domain: str, retry: int = 3) -> Set[Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]:
53
67
  res = set()
54
68
  if retry < 0:
@@ -171,6 +185,12 @@ def get_ip(version: Literal[4, 6], retry: int = 5) -> ipaddress.IPv4Address | ip
171
185
  return ip
172
186
 
173
187
 
188
+ def get_random_user_agent():
189
+
190
+ uas = requests.get('https://cdn.jsdelivr.net/gh/microlinkhq/top-user-agents@master/src/index.json').json()
191
+ if uas:
192
+ return random.sample(uas,1)[0]
193
+ return
174
194
  def get_random_domains(count: int = 1, retry: int = 3) -> List[str]:
175
195
  try:
176
196
  irurl = "https://api.ooni.io/api/v1/measurements?probe_cc=IR&test_name=web_connectivity&anomaly=false&confirmed=false&failure=false&order_by=test_start_time&limit=1000"
@@ -155,7 +155,7 @@ def to_clash(proxy, meta_or_normal):
155
155
  base["ws-opts"]["headers"] = {"Host": proxy["host"]}
156
156
 
157
157
  if base["network"] == "tcp" and proxy['alpn'] != 'h2':
158
- if proxy['transport'] != ProxyTransport.XTLS:
158
+ if proxy['transport'] != ProxyL3.reality:
159
159
  base["network"] = "http"
160
160
 
161
161
  if "path" in proxy:
@@ -11,11 +11,11 @@ from hiddifypanel.models import Proxy, ProxyProto, ProxyL3, ProxyTransport, Prox
11
11
  from hiddifypanel import hutils
12
12
 
13
13
 
14
- def get_ssh_hostkeys(hconfigs,dojson=False) -> list[str] | str:
14
+ def get_ssh_hostkeys(hconfigs, dojson=False) -> list[str] | str:
15
15
  host_keys = [
16
16
  # hconfigs[ConfigEnum.ssh_host_dsa_pub],
17
17
  hconfigs[ConfigEnum.ssh_host_ed25519_pub],
18
- hconfigs[ConfigEnum.ssh_host_ecdsa_pub],
18
+ hconfigs[ConfigEnum.ssh_host_ecdsa_pub],
19
19
  hconfigs[ConfigEnum.ssh_host_rsa_pub],
20
20
  ]
21
21
  if dojson:
@@ -28,13 +28,13 @@ def is_proxy_valid(proxy: Proxy, domain_db: Domain, port: int) -> dict | None:
28
28
  l3 = proxy.l3
29
29
  if not port:
30
30
  return {'name': name, 'msg': "port not defined", 'type': 'error', 'proto': proxy.proto}
31
- if "reality" not in l3 and domain_db.mode == DomainType.reality:
31
+ if "reality" not in l3 and 'reality' in domain_db.mode:
32
32
  return {'name': name, 'msg': "reality proxy not in reality domain", 'type': 'debug', 'proto': proxy.proto}
33
33
 
34
- if "reality" in l3 and domain_db.mode != DomainType.reality:
34
+ if "reality" in l3 and 'reality' not in domain_db.mode:
35
35
  return {'name': name, 'msg': "reality proxy not in reality domain", 'type': 'debug', 'proto': proxy.proto}
36
36
 
37
- if "reality" in l3 and domain_db.grpc and ProxyTransport.grpc != proxy.transport:
37
+ if "reality" in l3 and domain_db.mode in [DomainType.special_reality_grpc] and ProxyTransport.grpc != proxy.transport:
38
38
  return {'name': name, 'msg': "reality proxy not in reality domain", 'type': 'debug', 'proto': proxy.proto}
39
39
 
40
40
  if "reality" in l3 and (not domain_db.grpc) and ProxyTransport.grpc == proxy.transport:
@@ -58,18 +58,22 @@ def is_proxy_valid(proxy: Proxy, domain_db: Domain, port: int) -> dict | None:
58
58
  if domain_db.mode == DomainType.worker and proxy.transport == ProxyTransport.grpc:
59
59
  return {'name': name, 'msg': "worker does not support grpc", 'type': 'debug', 'proto': proxy.proto}
60
60
 
61
- if domain_db.mode != DomainType.old_xtls_direct and "tls" in proxy.l3 and proxy.cdn == ProxyCDN.direct and proxy.transport in [ProxyTransport.tcp, ProxyTransport.XTLS]:
62
- return {'name': name, 'msg': "only old_xtls_direct support this", 'type': 'debug', 'proto': proxy.proto}
61
+ if domain_db.mode == DomainType.old_xtls_direct:
62
+ return {'name': name, 'msg': "unsupported", 'type': 'debug', 'proto': proxy.proto}
63
+ # if domain_db.mode != DomainType.old_xtls_direct and "tls" in proxy.l3 and proxy.cdn == ProxyCDN.direct and proxy.transport in [ProxyTransport.tcp, ProxyTransport.XTLS]:
64
+ # return {'name': name, 'msg': "only old_xtls_direct support this", 'type': 'debug', 'proto': proxy.proto}
63
65
 
64
66
  if proxy.proto == "trojan" and not is_tls(l3):
65
67
  return {'name': name, 'msg': "trojan but not tls", 'type': 'warning', 'proto': proxy.proto}
66
68
 
67
- if l3 == "http" and ProxyTransport.XTLS in proxy.transport:
68
- return {'name': name, 'msg': "http and xtls???", 'type': 'warning', 'proto': proxy.proto}
69
+ # if l3 == "http" and ProxyTransport.XTLS in proxy.transport:
70
+ # return {'name': name, 'msg': "http and xtls???", 'type': 'warning', 'proto': proxy.proto}
69
71
 
70
72
  if l3 == "http" and proxy.proto in [ProxyProto.ss, ProxyProto.ssr]:
71
73
  return {'name': name, 'msg': "http and ss or ssr???", 'type': 'warning', 'proto': proxy.proto}
72
74
 
75
+ return
76
+
73
77
 
74
78
  def get_port(proxy: Proxy, hconfigs: dict, domain_db: Domain, ptls: int, phttp: int, pport: int | None) -> int:
75
79
  l3 = proxy.l3
@@ -235,6 +239,70 @@ def get_valid_proxies(domains: list[Domain]) -> list[dict]:
235
239
  return allp
236
240
 
237
241
 
242
+ def random_or_none(inp: list):
243
+ if not inp:
244
+ return
245
+ return random.sample(inp, 1)[0]
246
+
247
+
248
+ split_pattern = re.compile(r'[ \t\r\n;,]+')
249
+
250
+
251
+ def sni_host_server_extractor(domain_db: Domain, hconfigs):
252
+
253
+ server=sni=host = domain_db.domain.replace("*", hutils.random.get_random_string(5, 15))
254
+ is_cdn = domain_db.mode in [DomainType.cdn, DomainType.auto_cdn_ip]
255
+ if auto_ip:=domain_db.auto_cdn_ip():
256
+ server=auto_ip
257
+ elif 'special' in domain_db.mode.value or domain_db.mode in [DomainType.fake]:
258
+ server=hutils.network.get_direct_host_or_ip(4)
259
+
260
+ if hconfig(ConfigEnum.use_ip_in_config):
261
+ server = random_or_none(hutils.network.get_domain_ips_cached(server)) or server
262
+
263
+
264
+ allow_insecure=not domain_db.need_valid_ssl
265
+ if all_snis := split_pattern.split((domain_db.servernames or "").strip()):
266
+ sni = random_or_none(all_snis) or sni
267
+ if 'reality' in domain_db.mode:
268
+ allow_insecure=False
269
+ if hconfigs[ConfigEnum.core_type] == "singbox": #TODO
270
+ sni = all_snis[0]
271
+
272
+ else:
273
+ allow_insecure=True
274
+
275
+ base = {
276
+ 'sni': sni,
277
+ 'host': host,
278
+ 'server': server,
279
+ 'mode': domain_db.mode,
280
+ 'allow_insecure': allow_insecure,
281
+ 'cdn': is_cdn,
282
+ }
283
+ if 'reality' in domain_db.mode:
284
+ base['reality_short_id'] = random.sample(hconfigs[ConfigEnum.reality_short_ids].split(','), 1)[0]
285
+ # base['flow']="xtls-rprx-vision"
286
+ base['reality_pbk'] = hconfigs[ConfigEnum.reality_public_key]
287
+ # del base['host']
288
+
289
+ # if not domain_db.cdn_ip:
290
+ # base['server']=hiddify.get_domain_ip(base['server'])
291
+
292
+ return base
293
+
294
+ def put_default_header(parmas:dict):
295
+
296
+ if not isinstance(parmas.get('headers'),dict):
297
+ parmas['headers']={}
298
+
299
+ if not parmas['headers'].get('User-Agent'):
300
+ parmas['headers']['User-Agent']=hconfig(ConfigEnum.default_useragent_string)
301
+ if not parmas['headers'].get('Pragma'):
302
+ parmas['headers']['Pragma']="no-cache"
303
+
304
+
305
+
238
306
  def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=443, pport: int | None = None) -> dict:
239
307
 
240
308
  l3 = proxy.l3
@@ -254,17 +322,19 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
254
322
  if proxy.l3 in [ProxyL3.h3_quic]:
255
323
  alpn = "h3"
256
324
 
257
- cdn_forced_host = domain_db.cdn_ip or (domain_db.domain if domain_db.mode != DomainType.reality else hutils.network.get_direct_host_or_ip(4))
258
- is_cdn = ProxyCDN.CDN == proxy.cdn or ProxyCDN.Fake == proxy.cdn
325
+ # cdn_forced_host = domain_db.cdn_ip or (domain_db.domain if domain_db.mode != DomainType.reality else hutils.network.get_direct_host_or_ip(4))
326
+ # is_cdn = ProxyCDN.CDN == proxy.cdn or ProxyCDN.Fake == proxy.cdn
327
+ domain_data=sni_host_server_extractor(domain_db, hconfigs)
259
328
  base = {
329
+ **domain_data,
260
330
  'name': name,
261
- 'cdn': is_cdn,
262
- 'mode': "CDN" if is_cdn else "direct",
331
+ # 'cdn': is_cdn,
332
+ # 'mode': "CDN" if is_cdn else "direct",
263
333
  'l3': l3,
264
- 'host': domain,
334
+ # 'host': domain,
265
335
  'port': port,
266
- 'server': cdn_forced_host,
267
- 'sni': domain_db.servernames if is_cdn and domain_db.servernames else domain,
336
+ # 'server': cdn_forced_host,
337
+ # 'sni': domain_db.servernames if is_cdn and domain_db.servernames else domain,
268
338
  'uuid': str(g.account.uuid),
269
339
  'proto': proxy.proto,
270
340
  'transport': proxy.transport,
@@ -272,10 +342,29 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
272
342
  'alpn': alpn,
273
343
  'extra_info': f'{domain_db.alias or domain}',
274
344
  'fingerprint': hconfigs[ConfigEnum.utls],
275
- 'allow_insecure': domain_db.mode == DomainType.fake or "Fake" in proxy.cdn,
345
+ # 'allow_insecure': domain_db.mode == DomainType.fake or "Fake" in proxy.cdn,
276
346
  'dbe': proxy,
277
- 'dbdomain': domain_db
347
+ 'dbdomain': domain_db,
348
+ 'params': proxy.params or {},
278
349
  }
350
+ put_default_header(base['params'])
351
+
352
+
353
+ if domain_db.download_domain:
354
+ base['download'] = sni_host_server_extractor(domain_db.download_domain,hconfigs)
355
+ else:
356
+ base['download']=domain_data
357
+
358
+ if 'download' not in base['params']:
359
+ base['params']['download']={}
360
+ base['download']['params']=base['params']['download']
361
+ put_default_header(base['download']['params'])
362
+
363
+ base['download']['alpn']=base['params']['download'].get('alpn',alpn)
364
+
365
+
366
+
367
+
279
368
  if proxy.proto in ['tuic', 'hysteria2']:
280
369
  base['alpn'] = "h3"
281
370
  if proxy.proto == 'hysteria2':
@@ -297,36 +386,8 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
297
386
  if proxy.proto in [ProxyProto.vmess]:
298
387
  base['cipher'] = "auto" # "chacha20-poly1305"
299
388
 
300
- if l3 in ['reality']:
301
- base['reality_short_id'] = random.sample(hconfigs[ConfigEnum.reality_short_ids].split(','), 1)[0]
302
- # base['flow']="xtls-rprx-vision"
303
- base['reality_pbk'] = hconfigs[ConfigEnum.reality_public_key]
304
- if (domain_db.servernames):
305
- all_servernames = re.split('[ \t\r\n;,]+', domain_db.servernames)
306
- base['sni'] = random.sample(all_servernames, 1)[0]
307
- if hconfigs[ConfigEnum.core_type] == "singbox":
308
- base['sni'] = all_servernames[0]
309
- else:
310
- base['sni'] = domain_db.domain
311
-
312
- del base['host']
313
- if base.get('fingerprint'):
314
- base['fingerprint'] = hconfigs[ConfigEnum.utls]
315
- # if not domain_db.cdn_ip:
316
- # base['server']=hiddify.get_domain_ip(base['server'])
317
-
318
- if "Fake" in proxy.cdn:
319
- if not hconfigs[ConfigEnum.domain_fronting_domain]:
320
- return {'name': name, 'msg': "no domain_fronting_domain", 'type': 'debug', 'proto': proxy.proto}
321
- if l3 == "http" and not hconfigs[ConfigEnum.domain_fronting_http_enable]:
322
- return {'name': name, 'msg': "no http in domain_fronting_domain", 'type': 'debug', 'proto': proxy.proto}
323
- if l3 == "tls" and not hconfigs[ConfigEnum.domain_fronting_tls_enable]:
324
- return {'name': name, 'msg': "no tls in domain_fronting_domain", 'type': 'debug', 'proto': proxy.proto}
325
- base['server'] = hconfigs[ConfigEnum.domain_fronting_domain]
326
- base['sni'] = hconfigs[ConfigEnum.domain_fronting_domain]
327
- # base["host"]=domain
328
- base['mode'] = 'Fake'
329
- elif l3 == "http" and not hconfigs[ConfigEnum.http_proxy_enable]:
389
+
390
+ if l3 == "http" and not hconfigs[ConfigEnum.http_proxy_enable]:
330
391
  return {'name': name, 'msg': "http but http is disabled ", 'type': 'debug', 'proto': proxy.proto}
331
392
 
332
393
  path = {
@@ -361,7 +422,7 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
361
422
  return base
362
423
  elif "shadowsocks" in proxy.transport:
363
424
  return base
364
- if proxy.l3 in [ProxyL3.reality] and proxy.transport in [ProxyTransport.XTLS,ProxyTransport.xhttp]:
425
+ if proxy.l3 in [ProxyL3.reality] and proxy.transport in [ProxyTransport.tcp]:
365
426
  base['flow'] = 'xtls-rprx-vision'
366
427
  return {**base, 'transport': 'tcp'}
367
428
 
@@ -380,7 +441,7 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
380
441
  base['mux_brutal_up_mbps'] = hconfigs.get(ConfigEnum.mux_brutal_up_mbps, 10)
381
442
  base['mux_brutal_down_mbps'] = hconfigs.get(ConfigEnum.mux_brutal_down_mbps, 10)
382
443
 
383
- if is_cdn and proxy.proto in {'vless', 'trojan', "vmess"}:
444
+ if base['cdn'] and proxy.proto in {'vless', 'trojan', "vmess"}:
384
445
  if hconfigs[ConfigEnum.tls_fragment_enable] and "tls" in base["l3"]:
385
446
  base["tls_fragment_enable"] = True
386
447
  base["tls_fragment_size"] = hconfigs[ConfigEnum.tls_fragment_size]
@@ -402,6 +463,7 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
402
463
  base['transport'] = 'tcp'
403
464
  base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_tcp]}'
404
465
  return base
466
+
405
467
  if proxy.transport in ["ws", "WS"]:
406
468
  base['transport'] = 'ws'
407
469
  base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_ws]}'
@@ -413,14 +475,14 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
413
475
  base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_httpupgrade]}'
414
476
  base["host"] = domain
415
477
  return base
478
+
416
479
  if proxy.transport in [ProxyTransport.xhttp]:
417
480
  base['transport'] = 'xhttp'
418
481
  base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_xhttp]}'
419
- # if 0 and 'h2' in base['alpn'] or 'h3' in base['alpn']:
420
- # base['path'] += "2"
421
- # else:
422
- # base['path'] += "1"
423
- base["host"] = domain
482
+ base['xhttp_mode']=base['params'].get('mode',"auto")
483
+ if dl:=base.get('download'):
484
+ dl['path']=base['path']
485
+ dl['xhttp_mode']=dl['params'].get('mode',"auto")
424
486
  return base
425
487
 
426
488
  if proxy.transport == "grpc":
@@ -437,7 +499,7 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
437
499
  return base
438
500
  if ProxyProto.ssh == proxy.proto:
439
501
  base['private_key'] = g.account.ed25519_private_key
440
- base['host_keys'] = hutils.proxy.get_ssh_hostkeys(hconfigs,False)
502
+ base['host_keys'] = hutils.proxy.get_ssh_hostkeys(hconfigs, False)
441
503
  # base['ssh_port'] = hconfig(ConfigEnum.ssh_server_port)
442
504
  return base
443
505
  return {'name': name, 'msg': 'not valid', 'type': 'error', 'proto': proxy.proto}
@@ -4,7 +4,7 @@ from flask import request, g
4
4
  from hiddifypanel import hutils
5
5
  from hiddifypanel.models import ProxyTransport, ProxyL3, ProxyProto, Domain, User, ConfigEnum, hconfig
6
6
  from flask_babel import gettext as _
7
-
7
+ from urllib.parse import urlencode,quote
8
8
  OUTBOUND_LEVEL = 8
9
9
 
10
10
 
@@ -43,16 +43,18 @@ def to_link(proxy: dict) -> str | dict:
43
43
  "host": proxy.get("host", ""),
44
44
  "alpn": proxy.get("alpn", "h2,http/1.1"),
45
45
  "path": proxy["path"] if "path" in proxy else "",
46
- "tls": "tls" if "tls" in proxy["l3"] else "",
46
+ "tls": "tls" if "tls" in proxy["l3"] or "quic" in proxy['l3'] else "",
47
47
  "sni": proxy["sni"],
48
48
  "fp": proxy["fingerprint"]
49
49
  }
50
-
50
+
51
51
  if 'reality' in proxy["l3"]:
52
52
  vmess_data['tls'] = "reality"
53
53
  vmess_data['pbk'] = proxy['reality_pbk']
54
54
  vmess_data['sid'] = proxy['reality_short_id']
55
-
55
+ if proxy.get('transport') in {ProxyTransport.xhttp}:
56
+ vmess_data['core']='xray'
57
+ _add_xhttp_extra(vmess_data,proxy)
56
58
  add_tls_tricks_to_dict(vmess_data, proxy)
57
59
  add_mux_to_dict(vmess_data, proxy)
58
60
 
@@ -102,52 +104,70 @@ def to_link(proxy: dict) -> str | dict:
102
104
  # f'wg://{proxy["server"]}:{proxy["port"]}/?pk={proxy["wg_pk"]}&local_address={proxy["wg_ipv4"]}/32&peer_pk={proxy["wg_server_pub"]}&pre_shared_key={proxy["wg_psk"]}&workers=4&mtu=1380&reserved=0,0,0&ifp={proxy["wg_noise_trick"]}#{name_link}'
103
105
  return f'wg://{proxy["server"]}:{proxy["port"]}?publicKey={proxy["wg_pub"]}&privateKey={proxy["wg_pk"]}=&presharedKey={proxy["wg_psk"]}&ip=10.0.0.1&mtu=1380&keepalive=30&udp=1&reserved=0,0,0&ifp={proxy["wg_noise_trick"]}#{name_link}'
104
106
 
105
- baseurl = f'{proxy["proto"]}://{proxy["uuid"]}@{proxy["server"]}:{proxy["port"]}?hiddify=1'
106
- baseurl += f'&sni={proxy["sni"]}&type={proxy["transport"]}'
107
- baseurl += f"&alpn={proxy['alpn']}"
107
+
108
+ baseurl = f'{proxy["proto"]}://{proxy["uuid"]}@{proxy["server"]}:{proxy["port"]}'
109
+
110
+ q = {
111
+ 'hiddify': 1,
112
+ 'sni': proxy['sni'],
113
+ 'type': proxy['transport'],
114
+ 'alpn': proxy['alpn']
115
+ }
108
116
 
109
117
  # the ray2sing supports vless, vmess and trojan tls tricks and mux
110
118
  # the vmess handled already
111
119
 
112
- baseurl += add_mux_to_link(proxy)
113
- baseurl += add_tls_tricks_to_link(proxy)
114
-
120
+ add_mux_to_dict(q,proxy)
121
+ add_tls_tricks_to_dict(q,proxy)
122
+ if "path" in proxy:
123
+ q['path']=proxy["path"]
124
+ if "host" in proxy :
125
+ q['host']=proxy["host"]
115
126
  # infos+=f'&alpn={proxy["alpn"]}'
116
- baseurl += f'&path={proxy["path"]}' if "path" in proxy else ""
117
- baseurl += f'&host={proxy["host"]}' if "host" in proxy else ""
127
+
118
128
  if "grpc" == proxy["transport"]:
119
- baseurl += f'&serviceName={proxy["grpc_service_name"]}&mode={proxy["grpc_mode"]}'
129
+ q['serviceName']=proxy["grpc_service_name"]
130
+ q['mode']=proxy["grpc_mode"]
120
131
  # print(proxy['cdn'],proxy["transport"])
121
132
  if request.args.get("fragment"):
122
- baseurl += f'&fragment=' + request.args.get("fragment") # type: ignore
133
+ q['fragment']= request.args.get("fragment") # type: ignore
123
134
  if "ws" == proxy["transport"] and proxy['cdn'] and request.args.get("fragment_v1"):
124
- baseurl += f'&fragment_v1=' + request.args.get("fragment_v1") # type: ignore
135
+ q['fragment_v1']= request.args.get("fragment_v1") # type: ignore
125
136
  if 'vless' == proxy['proto']:
126
- baseurl += "&encryption=none"
137
+ q['encryption']='none'
138
+
127
139
  if proxy.get('fingerprint', 'none') != 'none':
128
- baseurl += "&fp=" + proxy['fingerprint']
140
+ q['fp']=proxy['fingerprint']
129
141
  if proxy.get('transport') in {ProxyTransport.xhttp}:
130
- baseurl += "&core=xray"
142
+ q['core']='xray'
143
+ _add_xhttp_extra(q,proxy)
131
144
  if proxy['l3'] != 'quic':
132
145
  if proxy.get('l3') != ProxyL3.reality and (proxy.get('transport') in {ProxyTransport.tcp, ProxyTransport.httpupgrade, ProxyTransport.xhttp}) and proxy['proto'] in [ProxyProto.vless, ProxyProto.trojan]:
133
- baseurl += '&headerType=http'
146
+ q['headerType']='http'
134
147
  else:
135
- baseurl += '&headerType=None'
148
+ q['headerType']='none'
136
149
 
137
150
  if proxy['mode'] == 'Fake' or proxy['allow_insecure']:
138
- baseurl += "&allowInsecure=true"
151
+ q['allowInsecure']='true'
152
+ q['insecure']='true'
139
153
  if proxy.get('flow'):
140
- baseurl += f'&flow={proxy["flow"]}'
141
-
142
- infos = f'#{name_link}'
154
+ q['flow']=proxy["flow"]
143
155
 
156
+
144
157
  if 'reality' in proxy["l3"]:
145
- return f"{baseurl}&security=reality&pbk={proxy['reality_pbk']}&sid={proxy['reality_short_id']}{infos}"
146
- if 'tls' in proxy['l3'] or "quic" in proxy['l3']:
147
- return f'{baseurl}&security=tls{infos}'
148
- if proxy['l3'] == 'http':
149
- return f'{baseurl}&security=none{infos}'
150
- return proxy
158
+ q['security']='reality'
159
+ q['pbk']=proxy['reality_pbk']
160
+ q['sid']=proxy['reality_short_id']
161
+
162
+
163
+ elif 'tls' in proxy['l3'] or "quic" in proxy['l3']:
164
+ q['security']='tls'
165
+
166
+ elif proxy['l3'] == 'http':
167
+ q['security']='none'
168
+
169
+ print(q)
170
+ return f"{baseurl}?{urlencode(q,quote_via=quote)}#{name_link}"
151
171
 
152
172
 
153
173
  def make_v2ray_configs(domains: list[Domain], user: User, expire_days: int, ip_debug=None) -> str:
@@ -230,13 +250,8 @@ def add_mux_to_dict(d: dict, proxy):
230
250
  d['muxdown'] = proxy["mux_brutal_down_mbps"]
231
251
 
232
252
 
233
- def add_tls_tricks_to_link(proxy: dict) -> str:
234
- out = {}
235
- add_tls_tricks_to_dict(out, proxy)
236
- return hutils.encode.convert_dict_to_url(out)
237
-
238
-
239
- def add_mux_to_link(proxy: dict) -> str:
240
- out = {}
241
- add_mux_to_dict(out, proxy)
242
- return hutils.encode.convert_dict_to_url(out)
253
+ def _add_xhttp_extra(d:dict,proxy):
254
+ from .xrayjson import _add_xhttp_details
255
+ xhttp_dict={}
256
+ _add_xhttp_details(xhttp_dict,proxy)
257
+ d['extra']=json.dumps(xhttp_dict['xhttpSettings']['extra'],separators=(',', ':'))