hiddifypanel 10.11.1__py3-none-any.whl → 10.12.0__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 (46) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/auth.py +15 -5
  4. hiddifypanel/hutils/encode.py +0 -1
  5. hiddifypanel/hutils/proxy/__init__.py +1 -0
  6. hiddifypanel/hutils/proxy/shared.py +18 -10
  7. hiddifypanel/hutils/proxy/singbox.py +22 -21
  8. hiddifypanel/hutils/proxy/xray.py +26 -352
  9. hiddifypanel/hutils/proxy/xrayjson.py +391 -0
  10. hiddifypanel/hutils/random.py +4 -0
  11. hiddifypanel/models/config.py +7 -2
  12. hiddifypanel/models/config_enum.py +9 -5
  13. hiddifypanel/panel/admin/DomainAdmin.py +3 -2
  14. hiddifypanel/panel/admin/templates/model/user_list.html +44 -20
  15. hiddifypanel/panel/commercial/restapi/v1/tgmsg.py +14 -10
  16. hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +17 -23
  17. hiddifypanel/panel/common_bp/login.py +2 -2
  18. hiddifypanel/panel/user/templates/base_xray_config.json.j2 +2 -2
  19. hiddifypanel/panel/user/user.py +1 -1
  20. hiddifypanel/static/images/hiddify.png +0 -0
  21. hiddifypanel/static/images/hiddify1.png +0 -0
  22. hiddifypanel/static/new/assets/hiddify-logo-7617d937.png +0 -0
  23. hiddifypanel/static/new/assets/hiddify-logo-7617d937_old.png +0 -0
  24. hiddifypanel/templates/admin-layout.html +22 -11
  25. hiddifypanel/templates/master.html +48 -25
  26. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  27. hiddifypanel/translations/en/LC_MESSAGES/messages.po +25 -16
  28. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  29. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +12 -5
  30. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  31. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +8 -5
  32. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  33. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +108 -47
  34. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  35. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +108 -55
  36. hiddifypanel/translations.i18n/en.json +15 -13
  37. hiddifypanel/translations.i18n/fa.json +4 -2
  38. hiddifypanel/translations.i18n/pt.json +3 -1
  39. hiddifypanel/translations.i18n/ru.json +50 -48
  40. hiddifypanel/translations.i18n/zh.json +58 -56
  41. {hiddifypanel-10.11.1.dist-info → hiddifypanel-10.12.0.dist-info}/METADATA +1 -1
  42. {hiddifypanel-10.11.1.dist-info → hiddifypanel-10.12.0.dist-info}/RECORD +46 -43
  43. {hiddifypanel-10.11.1.dist-info → hiddifypanel-10.12.0.dist-info}/LICENSE.md +0 -0
  44. {hiddifypanel-10.11.1.dist-info → hiddifypanel-10.12.0.dist-info}/WHEEL +0 -0
  45. {hiddifypanel-10.11.1.dist-info → hiddifypanel-10.12.0.dist-info}/entry_points.txt +0 -0
  46. {hiddifypanel-10.11.1.dist-info → hiddifypanel-10.12.0.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION CHANGED
@@ -1 +1 @@
1
- 10.11.1
1
+ 10.12.0
hiddifypanel/VERSION.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__='10.11.1'
1
+ __version__='10.12.0'
2
2
  from datetime import datetime
3
- __release_date__= datetime.strptime('2024-03-16','%Y-%m-%d')
3
+ __release_date__= datetime.strptime('2024-03-19','%Y-%m-%d')
hiddifypanel/auth.py CHANGED
@@ -143,9 +143,11 @@ def auth_before_request():
143
143
  # print(account)
144
144
  if not account:
145
145
  return logout_redirect()
146
-
147
- next_url = request.url.replace(f'/{g.uuid}/', '/admin/' if is_admin_path else '/client/').replace("/admin/admin/",
148
- '/admin/').replace("http://", "https://")
146
+ if is_admin_path:
147
+ next_url = request.url
148
+ next_url = next_url.replace(f'/{g.uuid}/', '/admin/')
149
+ next_url = next_url.replace("/admin/admin/", '/admin/')
150
+ next_url = next_url.replace("http://", "https://")
149
151
 
150
152
  elif apikey := request.headers.get("Hiddify-API-Key"):
151
153
  account = get_account_by_api_key(apikey, is_admin_path)
@@ -180,8 +182,16 @@ def auth_before_request():
180
182
  g.is_admin = hutils.flask.is_admin_role(account.role) # type: ignore
181
183
  login_user(account, force=True)
182
184
  # print("loggining in")
183
- if next_url is not None and g.user_agent['is_browser'] and ".webmanifest" not in request.path:
184
- return redirect(next_url)
185
+ if not g.is_admin:
186
+ return
187
+ if next_url is None:
188
+ return
189
+ if not g.user_agent['is_browser']:
190
+ return
191
+ if ".webmanifest" in request.path:
192
+ return
193
+
194
+ return redirect(next_url)
185
195
 
186
196
 
187
197
  def logout_redirect():
@@ -1,7 +1,6 @@
1
1
  import urllib.parse
2
2
  import base64
3
3
  import uuid
4
- import string
5
4
  from slugify import slugify
6
5
 
7
6
 
@@ -1,4 +1,5 @@
1
1
  from .shared import *
2
2
  from . import xray
3
+ from . import xrayjson
3
4
  from . import singbox
4
5
  from . import clash
@@ -259,6 +259,11 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
259
259
  }
260
260
  if proxy.proto in ['tuic', 'hysteria2']:
261
261
  base['alpn'] = "h3"
262
+ if proxy.proto == 'hysteria2':
263
+ base['hysteria_up_mbps'] = hconfigs.get(ConfigEnum.hysteria_up_mbps)
264
+ base['hysteria_down_mbps'] = hconfigs.get(ConfigEnum.hysteria_down_mbps)
265
+ base['hysteria_obfs_enable'] = hconfigs.get(ConfigEnum.hysteria_obfs_enable)
266
+ base['hysteria_obfs_password'] = hconfigs.get(ConfigEnum.proxy_path) # TODO: it should not be correct
262
267
  return base
263
268
  if proxy.proto in ['wireguard']:
264
269
  base['wg_pub'] = g.account.wg_pub
@@ -343,20 +348,18 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
343
348
 
344
349
  if proxy.proto in {'vless', 'trojan', 'vmess'} and hconfigs.get(ConfigEnum.mux_enable):
345
350
  if hconfigs[ConfigEnum.mux_enable]:
346
- base['mux_enable'] = True
347
- base['mux_protocol'] = hconfigs[ConfigEnum.mux_protocol]
348
- base['mux_max_connections'] = hconfigs[ConfigEnum.mux_max_connections]
349
- base['mux_min_streams'] = hconfigs[ConfigEnum.mux_min_streams]
350
- base['mux_max_streams'] = hconfigs[ConfigEnum.mux_max_streams]
351
- base['mux_padding_enable'] = hconfigs[ConfigEnum.mux_padding_enable]
351
+ base['mux_enable'] = hconfigs[ConfigEnum.core_type]
352
352
 
353
- # the hiddify next client doesn't support mux max streams
354
- base['mux_max_streams'] = hconfigs[ConfigEnum.mux_max_streams]
353
+ base['mux_protocol'] = hconfigs.get(ConfigEnum.mux_protocol, "h2mux")
354
+ base['mux_max_connections'] = hconfigs.get(ConfigEnum.mux_max_connections, 0)
355
+ base['mux_min_streams'] = hconfigs.get(ConfigEnum.mux_min_streams, 0)
356
+ base['mux_max_streams'] = hconfigs.get(ConfigEnum.mux_max_streams, 0)
357
+ base['mux_padding_enable'] = hconfigs.get(ConfigEnum.mux_padding_enable)
355
358
 
356
359
  if hconfigs[ConfigEnum.mux_brutal_enable]:
357
360
  base['mux_brutal_enable'] = True
358
- base['mux_brutal_up_mbps'] = hconfigs[ConfigEnum.mux_brutal_up_mbps]
359
- base['mux_brutal_down_mbps'] = hconfigs[ConfigEnum.mux_brutal_down_mbps]
361
+ base['mux_brutal_up_mbps'] = hconfigs.get(ConfigEnum.mux_brutal_up_mbps, 10)
362
+ base['mux_brutal_down_mbps'] = hconfigs.get(ConfigEnum.mux_brutal_down_mbps, 10)
360
363
 
361
364
  if is_cdn and proxy.proto in {'vless', 'trojan', "vmess"}:
362
365
  if hconfigs[ConfigEnum.tls_fragment_enable]:
@@ -366,6 +369,11 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
366
369
 
367
370
  if hconfigs[ConfigEnum.tls_mixed_case]:
368
371
  base["tls_mixed_case"] = hconfigs[ConfigEnum.tls_mixed_case]
372
+ base['host'] = hutils.random.random_case(base['host'])
373
+ base['sni'] = hutils.random.random_case(base['sni'])
374
+ base['server'] = hutils.random.random_case(base['server'])
375
+ if base.get('fakedomain'):
376
+ base['fakedomain'] = hutils.random.random_case(base['fakedomain'])
369
377
 
370
378
  if hconfigs[ConfigEnum.tls_padding_enable]:
371
379
  base["tls_padding_enable"] = hconfigs[ConfigEnum.tls_padding_enable]
@@ -2,7 +2,7 @@ from flask import render_template, request, g
2
2
  import json
3
3
 
4
4
  from hiddifypanel import hutils
5
- from hiddifypanel.models import ProxyProto, ProxyTransport, Domain, ConfigEnum, hconfig
5
+ from hiddifypanel.models import ProxyProto, ProxyTransport, Domain, ConfigEnum
6
6
  from hiddifypanel.panel.hiddify import is_hiddify_next_version
7
7
 
8
8
 
@@ -80,7 +80,7 @@ def to_singbox(proxy: dict) -> list[dict] | dict:
80
80
  base["uuid"] = proxy["uuid"]
81
81
 
82
82
  if proxy['proto'] in ['vmess', 'vless', 'trojan']:
83
- add_multiplex(base)
83
+ add_multiplex(base, proxy)
84
84
 
85
85
  add_tls(base, proxy)
86
86
 
@@ -109,32 +109,33 @@ def to_singbox(proxy: dict) -> list[dict] | dict:
109
109
  return all_base
110
110
 
111
111
 
112
- def add_multiplex(base: dict):
113
- if not hconfig(ConfigEnum.mux_enable):
112
+ def add_multiplex(base: dict, proxy: dict):
113
+ if proxy.get('mux_enable') != "singbox":
114
114
  return
115
115
  base['multiplex'] = {
116
116
  "enabled": True,
117
- "protocol": hconfig(ConfigEnum.mux_protocol),
118
- "padding": hconfig(ConfigEnum.mux_padding_enable)
117
+ "protocol": proxy['mux_protocol'],
118
+ "padding": proxy['mux_padding_enable']
119
119
  }
120
120
  # Conflicts: max_streams with max_connections and min_streams
121
- mux_max_streams = int(hconfig(ConfigEnum.mux_max_streams))
121
+ mux_max_streams = proxy.get('mux_max_streams', 0)
122
122
  if mux_max_streams and mux_max_streams != 0:
123
123
  base['multiplex']['max_streams'] = mux_max_streams
124
124
  else:
125
- base['multiplex']['max_connections'] = int(hconfig(ConfigEnum.mux_max_connections))
126
- base['multiplex']['min_streams'] = int(hconfig(ConfigEnum.mux_min_streams))
125
+ base['multiplex']['max_connections'] = proxy.get('mux_max_connections', 0)
126
+ base['multiplex']['min_streams'] = proxy.get('mux_min_streams', 0)
127
127
 
128
- add_tcp_brutal(base)
128
+ add_tcp_brutal(base,proxy)
129
129
 
130
130
 
131
- def add_tcp_brutal(base: dict):
131
+ def add_tcp_brutal(base: dict, proxy: dict):
132
132
  if 'multiplex' in base:
133
- base['multiplex']['brutal'] = {
134
- "enabled": hconfig(ConfigEnum.mux_brutal_enable),
135
- "up_mbps": int(hconfig(ConfigEnum.mux_brutal_up_mbps)),
136
- "down_mbps": int(hconfig(ConfigEnum.mux_brutal_down_mbps))
137
- }
133
+ if proxy.get('mux_brutal_enable'):
134
+ base['multiplex']['brutal'] = {
135
+ "enabled": proxy.get('mux_brutal_enable', False),
136
+ "up_mbps": proxy.get('mux_brutal_up_mbps', 10),
137
+ "down_mbps": proxy.get('mux_brutal_down_mbps', 10)
138
+ }
138
139
 
139
140
 
140
141
  def add_udp_over_tcp(base: dict):
@@ -261,7 +262,7 @@ def add_shadowsocks_base(all_base: list[dict], proxy: dict):
261
262
  base["method"] = proxy["cipher"]
262
263
  base["password"] = proxy["password"]
263
264
  add_udp_over_tcp(base)
264
- add_multiplex(base)
265
+ add_multiplex(base, proxy)
265
266
  if proxy["transport"] == "faketls":
266
267
  base["plugin"] = "obfs-local"
267
268
  base["plugin_opts"] = f'obfs=tls;obfs-host={proxy["fakedomain"]}'
@@ -327,12 +328,12 @@ def add_tuic(base: dict, proxy: dict):
327
328
 
328
329
 
329
330
  def add_hysteria(base: dict, proxy: dict):
330
- base['up_mbps'] = int(hconfig(ConfigEnum.hysteria_up_mbps))
331
- base['down_mbps'] = int(hconfig(ConfigEnum.hysteria_down_mbps))
331
+ base['up_mbps'] = proxy.get(ConfigEnum.hysteria_up_mbps)
332
+ base['down_mbps'] = proxy.get(ConfigEnum.hysteria_down_mbps)
332
333
  # TODO: check the obfs should be empty or not exists at all
333
- if hconfig(ConfigEnum.hysteria_obfs_enable):
334
+ if proxy.get('hysteria_obfs_enable'):
334
335
  base['obfs'] = {
335
336
  "type": "salamander",
336
- "password": hconfig(ConfigEnum.proxy_path)
337
+ "password": proxy.get('hysteria_obfs_password')
337
338
  }
338
339
  base['password'] = proxy['uuid']
@@ -3,12 +3,22 @@ import json
3
3
  import copy
4
4
  from flask import render_template, request, g
5
5
  from hiddifypanel import hutils
6
- from hiddifypanel.models import Proxy, ProxyTransport, ProxyL3, ProxyCDN, ProxyProto, Domain, hconfig, ConfigEnum, DomainType
6
+ from hiddifypanel.models import Proxy, ProxyTransport, ProxyL3, ProxyCDN, ProxyProto, Domain, ConfigEnum, DomainType, hconfig
7
7
  from flask_babel import gettext as _
8
8
 
9
9
  OUTBOUND_LEVEL = 8
10
10
 
11
11
 
12
+ def is_muxable_agent(proxy: dict) -> bool:
13
+ if not proxy.get('mux_enable'):
14
+ return False
15
+ if proxy.get('mux_enable') == "xray" and g.user_agent.get('is_singbox'):
16
+ return False
17
+ if proxy.get('mux_enable') == "singbox" and not g.user_agent.get('is_singbox'):
18
+ return False
19
+ return True
20
+
21
+
12
22
  def to_link(proxy: dict) -> str | dict:
13
23
  if 'error' in proxy:
14
24
  return proxy
@@ -38,6 +48,7 @@ def to_link(proxy: dict) -> str | dict:
38
48
  "sni": proxy["sni"],
39
49
  "fp": proxy["fingerprint"]
40
50
  }
51
+
41
52
  if 'reality' in proxy["l3"]:
42
53
  vmess_data['tls'] = "reality"
43
54
  vmess_data['pbk'] = proxy['reality_pbk']
@@ -77,7 +88,7 @@ def to_link(proxy: dict) -> str | dict:
77
88
  baseurl += "&allow_insecure=1"
78
89
  return f"{baseurl}#{name_link}"
79
90
  if proxy['proto'] == 'hysteria2':
80
- baseurl = f'hysteria2://{proxy["uuid"]}@{proxy["server"]}:{proxy["port"]}?hiddify=1&obfs=salamander&obfs-password={hconfig(ConfigEnum.proxy_path)}&sni={proxy["sni"]}'
91
+ baseurl = f'hysteria2://{proxy["uuid"]}@{proxy["server"]}:{proxy["port"]}?hiddify=1&obfs=salamander&obfs-password={proxy["hysteria_obfs_password"]}&sni={proxy["sni"]}'
81
92
  if proxy['mode'] == 'Fake' or proxy['allow_insecure']:
82
93
  baseurl += "&insecure=1"
83
94
  return f"{baseurl}#{name_link}"
@@ -183,345 +194,6 @@ def make_v2ray_configs(user, user_activate, domains: list[Domain], expire_days,
183
194
  return "\n".join(res)
184
195
 
185
196
 
186
- def configs_as_json(domains: list[Domain], remarks: str) -> str:
187
- '''Returns xray configs as json'''
188
- outbounds = []
189
- for proxy in hutils.proxy.get_valid_proxies(domains):
190
- outbound = to_xray(proxy)
191
- if 'msg' not in outbound:
192
- outbounds.append(outbound)
193
-
194
- outbounds_len = len(outbounds)
195
- # reutrn no outbound
196
- if outbounds_len < 1:
197
- return ''
198
-
199
- all_configs = []
200
- base_config = json.loads(render_template('base_xray_config.json.j2', remarks=remarks))
201
- # multiple outbounds needs multiple whole base config not just one with multiple outbounds (at least for v2rayng)
202
- # https://github.com/2dust/v2rayNG/pull/2827#issue-2127534078
203
- if outbounds_len > 1:
204
- for out in outbounds:
205
- base_config['remarks'] = out['tag']
206
- base_config['outbounds'].insert(0, out)
207
- all_configs.append(copy.deepcopy(base_config))
208
- del base_config['outbounds'][0]
209
- else: # single outbound
210
- base_config['outbounds'].insert(0, outbounds[0])
211
- all_configs = base_config
212
-
213
- json_configs = json.dumps(all_configs, indent=2, cls=hutils.proxy.ProxyJsonEncoder)
214
- return json_configs
215
-
216
-
217
- def to_xray(proxy: dict) -> dict:
218
- outbound = {
219
- 'tag': f'{proxy["extra_info"]} {proxy["name"]} § {proxy["port"]} {proxy["dbdomain"].id}',
220
- 'protocol': str(proxy['proto']),
221
- 'settings': {},
222
- 'streamSettings': {},
223
- 'mux': { # default value
224
- 'enabled': False,
225
- 'concurrency': -1
226
- }
227
- }
228
- # add multiplex to outbound
229
- add_multiplex(outbound)
230
-
231
- # add stream setting to outbound
232
- add_stream_settings(outbound, proxy)
233
-
234
- # add protocol settings to outbound
235
- add_proto_settings(outbound, proxy)
236
-
237
- return outbound
238
-
239
- # region proto settings
240
-
241
-
242
- def add_proto_settings(base: dict, proxy: dict):
243
- if proxy['proto'] == ProxyProto.wireguard:
244
- add_wireguard_settings(base, proxy)
245
- elif proxy['proto'] == ProxyProto.ss:
246
- add_shadowsocks_settings(base, proxy)
247
- elif proxy['proto'] == ProxyProto.vless:
248
- add_vless_settings(base, proxy)
249
- elif proxy['proto'] == ProxyProto.vmess:
250
- add_vmess_settings(base, proxy)
251
- elif proxy['proto'] == ProxyProto.trojan:
252
- proxy['password'] = proxy['uuid']
253
- add_trojan_settings(base, proxy)
254
-
255
-
256
- def add_wireguard_settings(base: dict, proxy: dict):
257
-
258
- base['settings']['secretKey'] = proxy['wg_pk']
259
- base['settings']['reversed'] = [0, 0, 0]
260
- base['settings']['mtu'] = 1380 # optional
261
- base['settings']['peers'] = [{
262
- 'endpoint': f'{proxy["server"]}:{int(proxy["port"])}',
263
- 'publicKey': proxy["wg_server_pub"]
264
- # 'allowedIPs':'', 'preSharedKey':'', 'keepAlive':'' # optionals
265
- }]
266
-
267
- # optionals
268
- # base['settings']['address'] = [f'{proxy["wg_ipv4"]}/32',f'{proxy["wg_ipv6"]}/128']
269
- # base['settings']['workers'] = 4
270
- # base['settings']['domainStrategy'] = 'ForceIP' # default
271
-
272
-
273
- def add_vless_settings(base: dict, proxy: dict):
274
- base['settings']['vnext'] = [
275
- {
276
- 'address': proxy['server'],
277
- 'port': proxy['port'],
278
- "users": [
279
- {
280
- 'id': proxy['uuid'],
281
- 'encryption': 'none',
282
- # 'security': 'auto',
283
- 'flow': 'xtls-rprx-vision' if (proxy['transport'] == ProxyTransport.XTLS or base['streamSettings']['security'] == 'reality') else '',
284
- 'level': OUTBOUND_LEVEL
285
- }
286
- ]
287
- }
288
- ]
289
-
290
-
291
- def add_vmess_settings(base: dict, proxy: dict):
292
- base['settings']['vnext'] = [
293
- {
294
- "address": proxy['server'],
295
- "port": proxy['port'],
296
- "users": [
297
- {
298
- "id": proxy['uuid'],
299
- "security": proxy['cipher'],
300
- "level": OUTBOUND_LEVEL
301
- }
302
- ]
303
- }
304
- ]
305
-
306
-
307
- def add_trojan_settings(base: dict, proxy: dict):
308
- base['settings']['servers'] = [
309
- {
310
- # 'email': proxy['uuid'], optional
311
- 'address': proxy['server'],
312
- 'port': proxy['port'],
313
- 'password': proxy['password'],
314
- 'level': OUTBOUND_LEVEL
315
- }
316
- ]
317
-
318
-
319
- def add_shadowsocks_settings(base: dict, proxy: dict):
320
- base['settings']['servers'] = [
321
- {
322
- 'address': proxy['server'],
323
- 'port': proxy['port'],
324
- 'method': proxy['cipher'],
325
- 'password': proxy['password'],
326
- 'uot': True,
327
- 'level': OUTBOUND_LEVEL
328
- # 'email': '', optional
329
- }
330
- ]
331
-
332
- # endregion
333
-
334
-
335
- # region stream settings
336
-
337
- def add_stream_settings(base: dict, proxy: dict):
338
- ss = base['streamSettings']
339
- ss['security'] = 'none' # default
340
-
341
- # security
342
- if proxy['l3'] == ProxyL3.reality:
343
- ss['security'] = 'reality'
344
- elif proxy['l3'] in [ProxyL3.tls, ProxyL3.tls_h2, ProxyL3.tls_h2_h1]:
345
- ss['security'] = 'tls'
346
-
347
- # network and transport settings
348
- if ss['security'] == 'tls' or 'xtls':
349
- ss['tlsSettings'] = {
350
- 'serverName': proxy['sni'],
351
- 'allowInsecure': proxy['allow_insecure'],
352
- 'fingerprint': proxy['fingerprint'],
353
- 'alpn': [proxy['alpn']],
354
- # 'minVersion': '1.2',
355
- # 'disableSystemRoot': '',
356
- # 'enableSessionResumption': '',
357
- # 'pinnedPeerCertificateChainSha256': '',
358
- # 'certificates': '',
359
- # 'maxVersion': '1.3', # Go lang sets
360
- # 'cipherSuites': '', # Go lang sets
361
- # 'rejectUnknownSni': '', # default is false
362
- }
363
- if ss['security'] == 'reality':
364
- ss['network'] = proxy['transport']
365
- add_reality_stream(ss, proxy)
366
- if proxy['l3'] == ProxyL3.kcp:
367
- ss['network'] = 'kcp'
368
- add_kcp_stream(ss, proxy)
369
-
370
- if proxy['l3'] == ProxyL3.h3_quic:
371
- add_quic_stream(ss, proxy)
372
-
373
- if proxy['transport'] == 'tcp' or ss['security'] == 'reality' or (ss['security'] == 'none' and proxy['transport'] not in [ProxyTransport.httpupgrade, ProxyTransport.WS]):
374
- ss['network'] = proxy['transport']
375
- add_tcp_stream(ss, proxy)
376
- if proxy['transport'] == ProxyTransport.h2 and ss['security'] == 'none' and ss['security'] != 'reality':
377
- ss['network'] = proxy['transport']
378
- add_http_stream(ss, proxy)
379
- if proxy['transport'] == ProxyTransport.grpc:
380
- ss['network'] = proxy['transport']
381
- add_grpc_stream(ss, proxy)
382
- if proxy['transport'] == ProxyTransport.httpupgrade:
383
- ss['network'] = proxy['transport']
384
- add_httpupgrade_stream(ss, proxy)
385
- if proxy['transport'] == 'ws':
386
- ss['network'] = proxy['transport']
387
- add_ws_stream(ss, proxy)
388
-
389
- # tls fragmentaion
390
- add_tls_fragmentation_stream_settings(base)
391
-
392
-
393
- def add_tcp_stream(ss: dict, proxy: dict):
394
- if proxy['l3'] == ProxyL3.http:
395
- ss['tcpSettings'] = {
396
- 'header': {
397
- 'type': 'http',
398
- 'request': {
399
- 'path': [proxy['path']]
400
- }
401
- }
402
- # 'acceptProxyProtocol': False
403
- }
404
- else:
405
- ss['tcpSettings'] = {
406
- 'header': {
407
- 'type': 'none'
408
- }
409
- # 'acceptProxyProtocol': False
410
- }
411
-
412
-
413
- def add_http_stream(ss: dict, proxy: dict):
414
- ss['httpSettings'] = {
415
- 'host': proxy['host'],
416
- 'path': proxy['path'],
417
- # 'read_idle_timeout': 10, # default disabled
418
- # 'health_check_timeout': 15, # default is 15
419
- # 'method': 'PUT', # default is 15
420
- # 'headers': {
421
-
422
- # }
423
- }
424
-
425
-
426
- def add_ws_stream(ss: dict, proxy: dict):
427
- ss['wsSettings'] = {
428
- 'path': proxy['path'],
429
- 'headers': {
430
- "Host": proxy['host']
431
- }
432
- # 'acceptProxyProtocol': False,
433
- }
434
-
435
-
436
- def add_grpc_stream(ss: dict, proxy: dict):
437
- ss['grpcSettings'] = {
438
- 'serviceName': proxy['path'], # proxy['path'] is equal toproxy['grpc_service_name']
439
- 'idle_timeout': 115, # by default, the health check is not enabled. may solve some "connection drop" issues
440
- 'health_check_timeout': 20, # default is 20
441
- # 'initial_windows_size': 0, # 0 means disabled. greater than 65535 means Dynamic Window mechanism will be disabled
442
- # 'permit_without_stream': False, # health check performed when there are no sub-connections
443
- # 'multiMode': false, # experimental
444
- }
445
-
446
-
447
- def add_httpupgrade_stream(ss: dict, proxy: dict):
448
- ss['httpupgradeSettings'] = {
449
- 'path': proxy['path'],
450
- 'host': proxy['host'],
451
- # 'acceptProxyProtocol': '', for inbounds only
452
- }
453
-
454
-
455
- def add_kcp_stream(ss: dict, proxy: dict):
456
- # TODO: fix server side configs first
457
- ss['kcpSettings'] = {}
458
- return
459
- ss['kcpSettings'] = {
460
- 'seed': proxy['path'],
461
- # 'mtu': 1350, # optional, default value is written
462
- # 'tti': 50, # optional, default value is written
463
- # 'uplinkCapacity': 5, # optional, default value is written
464
- # 'downlinkCapacity': 20, # optional, default value is written
465
- # 'congestion':False, # optional, default value is written
466
- # 'readBufferSize': 2,# optional, default value is written
467
- # 'writeBufferSize':2 # optional, default value is written
468
- # 'header': { # must be same as server (hiddify doesn't use yet)
469
- # 'type': 'none' # choices: none(default), srtp, utp, wechat-video, dtls, wireguards
470
- # }
471
- }
472
-
473
-
474
- def add_quic_stream(ss: dict, proxy: dict):
475
- # TODO: fix server side configs first
476
- ss['quicSettings'] = {}
477
- return
478
- ss['quicSettings'] = {
479
- 'security': 'chacha20-poly1305',
480
- 'key': proxy['path'],
481
- 'header': {
482
- 'type': 'none'
483
- }
484
- }
485
-
486
-
487
- def add_reality_stream(ss: dict, proxy: dict):
488
- ss['realitySettings'] = {
489
- 'serverName': proxy['sni'],
490
- 'fingerprint': proxy['fingerprint'],
491
- 'shortId': proxy['reality_short_id'],
492
- 'publicKey': proxy['reality_pbk'],
493
- 'show': False,
494
- }
495
-
496
-
497
- def add_tls_fragmentation_stream_settings(base: dict):
498
- '''Adds tls fragment in the outbounds if tls fragmentation is enabled'''
499
- if base['streamSettings']['security'] in ['tls', 'reality']:
500
- if hconfig(ConfigEnum.tls_fragment_enable):
501
- base['streamSettings']['sockopt'] = {
502
- 'dialerProxy': 'fragment',
503
- 'tcpKeepAliveIdle': 100,
504
- 'tcpNoDelay': True, # recommended to be enabled with "tcpMptcp": true.
505
- "mark": 255
506
- # 'tcpFastOpen': True, # the system default setting be used.
507
- # 'tcpKeepAliveInterval': 0, # 0 means default GO lang settings, -1 means not enable
508
- # 'tcpcongestion': bbr, # Not configuring means using the system default value
509
- # 'tcpMptcp': True, # need to be enabled in both server and client configuration (not supported by panel yet)
510
- }
511
-
512
- # endregion
513
-
514
-
515
- def add_multiplex(base: dict):
516
- if hconfig(ConfigEnum.mux_enable):
517
- concurrency = hutils.convert.to_int(hconfig(ConfigEnum.mux_max_connections))
518
- if concurrency and concurrency > 0:
519
- base['mux']['enabled'] = True
520
- base['mux']['concurrency'] = concurrency
521
- base['mux']['xudpConcurrency'] = concurrency
522
- base['mux']['xudpProxyUDP443'] = 'reject'
523
-
524
-
525
197
  def add_tls_tricks_to_dict(d: dict, proxy: dict):
526
198
  if proxy.get('tls_fragment_enable'):
527
199
  if g.user_agent.get('is_shadowrocket'):
@@ -536,17 +208,19 @@ def add_tls_tricks_to_dict(d: dict, proxy: dict):
536
208
 
537
209
 
538
210
  def add_mux_to_dict(d: dict, proxy):
539
- if proxy.get('mux_enable'):
540
- # according to github.com/hiddify/ray2sing/
541
- d['muxtype'] = proxy["mux_protocol"]
542
- d['muxmaxc'] = proxy["mux_max_connections"]
543
- d['mux'] = proxy['mux_min_streams']
544
- d['muxsmax'] = proxy["mux_max_streams"]
545
- d['muxpad'] = proxy["mux_padding_enable"]
546
-
547
- if proxy.get('mux_brutal_enable'):
548
- d['muxup'] = proxy["mux_brutal_up_mbps"]
549
- d['muxdown'] = proxy["mux_brutal_down_mbps"]
211
+ if not is_muxable_agent(proxy):
212
+ return
213
+
214
+ # according to github.com/hiddify/ray2sing/
215
+ d['muxtype'] = proxy["mux_protocol"]
216
+ d['muxmaxc'] = proxy["mux_max_connections"]
217
+ d['mux'] = proxy['mux_min_streams']
218
+ d['muxsmax'] = proxy["mux_max_streams"]
219
+ d['muxpad'] = proxy["mux_padding_enable"]
220
+
221
+ if proxy.get('mux_brutal_enable'):
222
+ d['muxup'] = proxy["mux_brutal_up_mbps"]
223
+ d['muxdown'] = proxy["mux_brutal_down_mbps"]
550
224
 
551
225
 
552
226
  def add_tls_tricks_to_link(proxy: dict) -> str: