hiddifypanel 9.0.0.dev92__py3-none-any.whl → 10.5.0.dev0__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 (152) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/auth.py +30 -9
  4. hiddifypanel/base.py +58 -50
  5. hiddifypanel/cache.py +43 -25
  6. hiddifypanel/database.py +9 -0
  7. hiddifypanel/drivers/abstract_driver.py +2 -0
  8. hiddifypanel/drivers/singbox_api.py +17 -15
  9. hiddifypanel/drivers/ssh_liberty_bridge_api.py +3 -1
  10. hiddifypanel/drivers/user_driver.py +12 -6
  11. hiddifypanel/drivers/wireguard_api.py +7 -2
  12. hiddifypanel/drivers/xray_api.py +14 -9
  13. hiddifypanel/hutils/__init__.py +4 -0
  14. hiddifypanel/hutils/convert.py +13 -2
  15. hiddifypanel/hutils/crypto.py +48 -0
  16. hiddifypanel/hutils/encode.py +4 -1
  17. hiddifypanel/hutils/flask.py +38 -5
  18. hiddifypanel/hutils/github_issue.py +1 -1
  19. hiddifypanel/hutils/importer/xui.py +5 -2
  20. hiddifypanel/{models/utils.py → hutils/model.py} +14 -4
  21. hiddifypanel/hutils/network/auto_ip_selector.py +2 -0
  22. hiddifypanel/hutils/network/net.py +46 -2
  23. hiddifypanel/hutils/node/__init__.py +3 -0
  24. hiddifypanel/hutils/node/api_client.py +76 -0
  25. hiddifypanel/hutils/node/child.py +147 -0
  26. hiddifypanel/hutils/node/parent.py +100 -0
  27. hiddifypanel/hutils/node/shared.py +65 -0
  28. hiddifypanel/hutils/proxy/__init__.py +5 -0
  29. hiddifypanel/hutils/proxy/clash.py +161 -0
  30. hiddifypanel/hutils/proxy/shared.py +434 -0
  31. hiddifypanel/hutils/proxy/singbox.py +339 -0
  32. hiddifypanel/hutils/proxy/xray.py +235 -0
  33. hiddifypanel/hutils/proxy/xrayjson.py +391 -0
  34. hiddifypanel/hutils/random.py +4 -0
  35. hiddifypanel/hutils/utils.py +4 -1
  36. hiddifypanel/models/__init__.py +2 -2
  37. hiddifypanel/models/admin.py +31 -17
  38. hiddifypanel/models/base_account.py +7 -7
  39. hiddifypanel/models/child.py +30 -16
  40. hiddifypanel/models/config.py +45 -16
  41. hiddifypanel/models/config_enum.py +68 -17
  42. hiddifypanel/models/domain.py +28 -20
  43. hiddifypanel/models/parent_domain.py +2 -2
  44. hiddifypanel/models/proxy.py +29 -20
  45. hiddifypanel/models/report.py +2 -3
  46. hiddifypanel/models/usage.py +2 -2
  47. hiddifypanel/models/user.py +33 -22
  48. hiddifypanel/panel/admin/Actions.py +13 -19
  49. hiddifypanel/panel/admin/AdminstratorAdmin.py +14 -3
  50. hiddifypanel/panel/admin/Dashboard.py +5 -10
  51. hiddifypanel/panel/admin/DomainAdmin.py +35 -48
  52. hiddifypanel/panel/admin/NodeAdmin.py +6 -2
  53. hiddifypanel/panel/admin/ProxyAdmin.py +6 -5
  54. hiddifypanel/panel/admin/QuickSetup.py +21 -20
  55. hiddifypanel/panel/admin/SettingAdmin.py +107 -62
  56. hiddifypanel/panel/admin/UserAdmin.py +22 -21
  57. hiddifypanel/panel/admin/templates/index.html +1 -1
  58. hiddifypanel/panel/admin/templates/model/user_list.html +44 -20
  59. hiddifypanel/panel/admin/templates/parent_dash.html +2 -4
  60. hiddifypanel/panel/admin/templates/result.html +2 -3
  61. hiddifypanel/panel/cf_api.py +1 -2
  62. hiddifypanel/panel/cli.py +16 -16
  63. hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +16 -12
  64. hiddifypanel/panel/commercial/__init__.py +7 -5
  65. hiddifypanel/panel/commercial/restapi/v1/__init__.py +1 -1
  66. hiddifypanel/panel/commercial/restapi/v1/tgbot.py +1 -1
  67. hiddifypanel/panel/commercial/restapi/v1/tgmsg.py +14 -10
  68. hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +0 -5
  69. hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +2 -2
  70. hiddifypanel/panel/commercial/restapi/v2/admin/admin_log_api.py +4 -5
  71. hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +8 -25
  72. hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +4 -4
  73. hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +157 -0
  74. hiddifypanel/panel/commercial/restapi/v2/admin/server_status_api.py +3 -3
  75. hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +9 -66
  76. hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -1
  77. hiddifypanel/panel/commercial/restapi/v2/child/__init__.py +18 -0
  78. hiddifypanel/panel/commercial/restapi/v2/child/actions.py +63 -0
  79. hiddifypanel/panel/commercial/restapi/v2/child/register_parent_api.py +34 -0
  80. hiddifypanel/panel/commercial/restapi/v2/child/schema.py +7 -0
  81. hiddifypanel/panel/commercial/restapi/v2/child/sync_parent_api.py +21 -0
  82. hiddifypanel/panel/commercial/restapi/v2/panel/__init__.py +13 -0
  83. hiddifypanel/panel/commercial/restapi/v2/panel/info.py +18 -0
  84. hiddifypanel/panel/commercial/restapi/v2/panel/ping_pong.py +23 -0
  85. hiddifypanel/panel/commercial/restapi/v2/panel/schema.py +7 -0
  86. hiddifypanel/panel/commercial/restapi/v2/parent/__init__.py +16 -0
  87. hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +65 -0
  88. hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +115 -0
  89. hiddifypanel/panel/commercial/restapi/v2/parent/status_api.py +26 -0
  90. hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +53 -0
  91. hiddifypanel/panel/commercial/restapi/v2/parent/usage_api.py +57 -0
  92. hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +17 -23
  93. hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +23 -26
  94. hiddifypanel/panel/commercial/telegrambot/admin.py +1 -2
  95. hiddifypanel/panel/common.py +25 -8
  96. hiddifypanel/panel/common_bp/login.py +2 -2
  97. hiddifypanel/panel/hiddify.py +22 -185
  98. hiddifypanel/panel/init_db.py +102 -55
  99. hiddifypanel/panel/usage.py +33 -18
  100. hiddifypanel/panel/user/__init__.py +0 -1
  101. hiddifypanel/panel/user/templates/all_configs copy.txt +2 -2
  102. hiddifypanel/panel/user/templates/all_configs.txt +2 -2
  103. hiddifypanel/panel/user/templates/base_singbox_config.json.j2 +2 -1
  104. hiddifypanel/panel/user/templates/base_xray_config.json.j2 +125 -0
  105. hiddifypanel/panel/user/templates/clash_config copy.yml +1 -1
  106. hiddifypanel/panel/user/templates/clash_config.yml +4 -4
  107. hiddifypanel/panel/user/templates/clash_proxies.yml +1 -1
  108. hiddifypanel/panel/user/templates/home/all-configs.html +2 -2
  109. hiddifypanel/panel/user/templates/home/all-configs_old.html +1 -1
  110. hiddifypanel/panel/user/templates/home/ios copy.html +2 -2
  111. hiddifypanel/panel/user/templates/home/usage.html +1 -1
  112. hiddifypanel/panel/user/templates/new.html +2 -2
  113. hiddifypanel/panel/user/user.py +56 -50
  114. hiddifypanel/static/css/custom.css +31 -0
  115. hiddifypanel/static/images/favicon.ico +0 -0
  116. hiddifypanel/static/images/hiddify-old.png +0 -0
  117. hiddifypanel/static/images/hiddify.png +0 -0
  118. hiddifypanel/static/images/hiddify2.png +0 -0
  119. hiddifypanel/static/new/assets/{index-1b891a7c.js → index-ccb9873c.js} +56 -56
  120. hiddifypanel/static/new/assets/index-fa00de9a.css +1 -0
  121. hiddifypanel/static/new/i18n/en.json +6 -6
  122. hiddifypanel/static/new/i18n/fa.json +2 -2
  123. hiddifypanel/templates/admin-layout.html +30 -43
  124. hiddifypanel/templates/fake.html +0 -4
  125. hiddifypanel/templates/flaskadmin-layout.html +7 -3
  126. hiddifypanel/templates/master.html +11 -6
  127. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  128. hiddifypanel/translations/en/LC_MESSAGES/messages.po +2082 -1977
  129. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  130. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +2035 -1924
  131. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  132. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +1911 -1848
  133. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  134. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +2019 -1874
  135. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  136. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +1873 -1742
  137. hiddifypanel/translations.i18n/en.json +992 -933
  138. hiddifypanel/translations.i18n/fa.json +994 -935
  139. hiddifypanel/translations.i18n/pt.json +1031 -972
  140. hiddifypanel/translations.i18n/ru.json +994 -935
  141. hiddifypanel/translations.i18n/zh.json +971 -912
  142. {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/METADATA +47 -47
  143. {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/RECORD +147 -120
  144. {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/WHEEL +1 -1
  145. hiddifypanel/panel/commercial/restapi/v2/DTO.py +0 -9
  146. hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py +0 -16
  147. hiddifypanel/panel/commercial/restapi/v2/hello/hello.py +0 -32
  148. hiddifypanel/panel/user/link_maker.py +0 -1083
  149. hiddifypanel/static/new/assets/index-669b32c8.css +0 -1
  150. {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/LICENSE.md +0 -0
  151. {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/entry_points.txt +0 -0
  152. {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,5 @@
1
+ from .shared import *
2
+ from . import xray
3
+ from . import xrayjson
4
+ from . import singbox
5
+ from . import clash
@@ -0,0 +1,161 @@
1
+ import yaml
2
+ from hiddifypanel.models import Proxy, ProxyCDN, ProxyL3, ProxyProto, ProxyTransport, Domain
3
+ from hiddifypanel import hutils
4
+
5
+
6
+ def get_clash_config_names(meta_or_normal, domains: list[Domain]):
7
+ allp = []
8
+ for pinfo in hutils.proxy.get_valid_proxies(domains):
9
+ clash = to_clash(pinfo, meta_or_normal)
10
+ if 'msg' not in clash:
11
+ allp.append(clash['name'])
12
+
13
+ return yaml.dump(allp, sort_keys=False)
14
+
15
+
16
+ def get_all_clash_configs(meta_or_normal, domains: list[Domain]):
17
+ allp = []
18
+ for pinfo in hutils.proxy.get_valid_proxies(domains):
19
+ clash = to_clash(pinfo, meta_or_normal)
20
+ if 'msg' not in clash:
21
+ allp.append(clash)
22
+
23
+ return yaml.dump({"proxies": allp}, sort_keys=False)
24
+
25
+ # def to_clash_yml(proxy):
26
+ # return yaml.dump(to_clash(proxy,'normal'))
27
+
28
+
29
+ def to_clash(proxy, meta_or_normal):
30
+
31
+ name = proxy['name']
32
+ if proxy['l3'] == "kcp":
33
+ return {'name': name, 'msg': "clash does not support kcp", 'type': 'debug'}
34
+ if proxy['proto'] == "ssh":
35
+ return {'name': name, 'msg': "clash does not support ssh", 'type': 'debug'}
36
+ if meta_or_normal == "normal":
37
+ if proxy['proto'] in ["vless", 'tuic', 'hysteria2']:
38
+ return {'name': name, 'msg': f"{proxy['proto']} not supported in clash", 'type': 'debug'}
39
+ if proxy.get('flow'):
40
+ return {'name': name, 'msg': "xtls not supported in clash", 'type': 'debug'}
41
+ if proxy['transport'] == "shadowtls":
42
+ return {'name': name, 'msg': "shadowtls not supported in clash", 'type': 'debug'}
43
+ if proxy['l3'] == ProxyL3.tls_h2 and proxy['proto'] in [ProxyProto.vmess, ProxyProto.vless] and proxy['dbe'].cdn == ProxyCDN.direct:
44
+ return {'name': name, 'msg': "bug tls_h2 vmess and vless in clash meta", 'type': 'warning'}
45
+ base = {}
46
+ # vmess ws
47
+ base["name"] = f"""{proxy['extra_info']} {proxy["name"]} § {proxy['port']} {proxy["dbdomain"].id}"""
48
+ base["type"] = str(proxy["proto"])
49
+ base["server"] = proxy["server"]
50
+ base["port"] = proxy["port"]
51
+ base['alpn'] = proxy['alpn'].split(',')
52
+ if proxy["proto"] == "ssr":
53
+ base["cipher"] = proxy["cipher"]
54
+ base["password"] = proxy["uuid"]
55
+ base["udp"] = True
56
+ base["obfs"] = proxy["ssr-obfs"]
57
+ base["protocol"] = proxy["ssr-protocol"]
58
+ base["obfs-param"] = proxy["fakedomain"]
59
+ return base
60
+ elif proxy["proto"] == "tuic":
61
+ base["uuid"] = proxy["uuid"]
62
+ base["password"] = proxy["uuid"]
63
+ base["disable-sni"] = proxy['allow_insecure']
64
+ base["reduce-rtt"] = True
65
+ base["request-timeout"] = 8000
66
+ base["udp-relay-mode"] = 'native'
67
+ base["congestion-controller"] = 'cubic'
68
+ base['sni'] = proxy['sni']
69
+ return base
70
+ elif proxy["proto"] in ["ss", "v2ray"]:
71
+ base["cipher"] = proxy["cipher"]
72
+ base["password"] = proxy["password"]
73
+ base["udp_over_tcp"] = True
74
+ if proxy["transport"] == "faketls":
75
+ base["plugin"] = "obfs"
76
+ base["plugin-opts"] = {
77
+ "mode": 'tls',
78
+ "host": proxy["fakedomain"]
79
+ }
80
+ elif proxy["transport"] == "shadowtls":
81
+ base["plugin"] = "shadow-tls"
82
+ base["plugin-opts"] = {
83
+ "host": proxy["fakedomain"],
84
+ "password": proxy["proxy_path"],
85
+ "version": 3 # support 1/2/3
86
+
87
+ }
88
+
89
+ elif proxy["proto"] == "v2ray":
90
+ base["plugin"] = "v2ray-plugin"
91
+ base["type"] = "ss"
92
+ base["plugin-opts"] = {
93
+ "mode": "websocket",
94
+ "tls": "tls" in proxy["l3"],
95
+ "skip-cert-verify": proxy["mode"] == "Fake" or proxy['allow_insecure'],
96
+ "host": proxy['sni'],
97
+ "path": proxy["path"]
98
+ }
99
+ return base
100
+ elif proxy["proto"] == "trojan":
101
+ base["password"] = proxy["uuid"]
102
+ base["sni"] = proxy["sni"]
103
+
104
+ else:
105
+ base["uuid"] = proxy["uuid"]
106
+ base["servername"] = proxy["sni"]
107
+ base["tls"] = "tls" in proxy["l3"] or "reality" in proxy["l3"]
108
+ if meta_or_normal == "meta":
109
+ base['client-fingerprint'] = proxy['fingerprint']
110
+ if proxy.get('flow'):
111
+ base["flow"] = proxy['flow']
112
+ # base["flow-show"] = True
113
+
114
+ if proxy["proto"] == "vmess":
115
+ base["alterId"] = 0
116
+ base["cipher"] = proxy["cipher"]
117
+ base["udp"] = True
118
+
119
+ base["skip-cert-verify"] = proxy["mode"] == "Fake"
120
+
121
+ base["network"] = proxy["transport"]
122
+
123
+ if base["network"] == "ws":
124
+ base["ws-opts"] = {
125
+ "path": proxy["path"]
126
+ }
127
+ if "host" in proxy:
128
+ base["ws-opts"]["headers"] = {"Host": proxy["host"]}
129
+
130
+ if base["network"] == "tcp" and proxy['alpn'] != 'h2':
131
+ if proxy['transport'] != ProxyTransport.XTLS:
132
+ base["network"] = "http"
133
+
134
+ if "path" in proxy:
135
+ base["http-opts"] = {
136
+ "path": [proxy["path"]]
137
+ }
138
+ if 'host' in proxy:
139
+ base["http-opts"]["host"] = [proxy["host"]]
140
+ if base["network"] == "tcp" and proxy['alpn'] == 'h2':
141
+ base["network"] = "h2"
142
+
143
+ if "path" in proxy:
144
+ base["h2-opts"] = {
145
+ "path": proxy["path"]
146
+ }
147
+ if 'host' in proxy:
148
+ base["h2-opts"]["host"] = [proxy["host"]]
149
+ if base["network"] == "grpc":
150
+ base["grpc-opts"] = {
151
+ "grpc-service-name": proxy["grpc_service_name"]
152
+ }
153
+ if proxy['l3'] == ProxyL3.reality:
154
+ base["reality-opts"] = {
155
+ "public-key": proxy['reality_pbk'],
156
+ "short-id": proxy['reality_short_id'],
157
+ }
158
+ if proxy["transport"] != 'grpc':
159
+ base["network"] = 'tcp'
160
+
161
+ return base
@@ -0,0 +1,434 @@
1
+ from flask import current_app, request, g
2
+ import glob
3
+ import random
4
+ import re
5
+ import json
6
+ from ipaddress import IPv4Address, IPv6Address
7
+ from hiddifypanel.cache import cache
8
+ from hiddifypanel.models import Proxy, ProxyProto, ProxyL3, ProxyTransport, ProxyCDN, Domain, DomainType, ConfigEnum, hconfig, get_hconfigs
9
+ from hiddifypanel import hutils
10
+
11
+
12
+ def get_ssh_hostkeys(dojson=False) -> list[str] | str:
13
+ key_files = glob.glob(current_app.config['HIDDIFY_CONFIG_PATH'] + "/other/ssh/host_key/*_key.pub")
14
+ host_keys = []
15
+ for file_name in key_files:
16
+ with open(file_name, "r") as f:
17
+ host_key = f.read().strip()
18
+ host_key = host_key.split()
19
+ if len(host_key) > 2:
20
+ host_key = host_key[:2] # strip the hostname part
21
+ host_key = " ".join(host_key)
22
+ host_keys.append(host_key)
23
+ if dojson:
24
+ return json.dumps(host_keys)
25
+ return host_keys
26
+
27
+
28
+ def is_proxy_valid(proxy: Proxy, domain_db: Domain, port: int) -> dict | None:
29
+ name = proxy.name
30
+ l3 = proxy.l3
31
+ if not port:
32
+ return {'name': name, 'msg': "port not defined", 'type': 'error', 'proto': proxy.proto}
33
+ if "reality" not in l3 and domain_db.mode == DomainType.reality:
34
+ return {'name': name, 'msg': "reality proxy not in reality domain", 'type': 'debug', 'proto': proxy.proto}
35
+
36
+ if "reality" in l3 and domain_db.mode != DomainType.reality:
37
+ return {'name': name, 'msg': "reality proxy not in reality domain", 'type': 'debug', 'proto': proxy.proto}
38
+
39
+ if "reality" in l3 and domain_db.grpc and ProxyTransport.grpc != proxy.transport:
40
+ return {'name': name, 'msg': "reality proxy not in reality domain", 'type': 'debug', 'proto': proxy.proto}
41
+
42
+ if "reality" in l3 and (not domain_db.grpc) and ProxyTransport.grpc == proxy.transport:
43
+ return {'name': name, 'msg': "reality proxy not in reality domain", 'type': 'debug', 'proto': proxy.proto}
44
+
45
+ is_cdn = ProxyCDN.CDN == proxy.cdn or ProxyCDN.Fake == proxy.cdn
46
+ if is_cdn and domain_db.mode not in [DomainType.cdn, DomainType.auto_cdn_ip, DomainType.worker]:
47
+ # print("cdn proxy not in cdn domain", domain, name)
48
+ return {'name': name, 'msg': "cdn proxy not in cdn domain", 'type': 'debug', 'proto': proxy.proto}
49
+
50
+ if not is_cdn and domain_db.mode in [DomainType.cdn, DomainType.auto_cdn_ip, DomainType.worker]:
51
+ # print("not cdn proxy in cdn domain", domain, name, proxy.cdn)
52
+ return {'name': name, 'msg': "not cdn proxy in cdn domain", 'type': 'debug', 'proto': proxy.proto}
53
+
54
+ if proxy.cdn == ProxyCDN.relay and domain_db.mode not in [DomainType.relay]:
55
+ return {'name': name, 'msg': "relay proxy not in relay domain", 'type': 'debug', 'proto': proxy.proto}
56
+
57
+ if proxy.cdn != ProxyCDN.relay and domain_db.mode in [DomainType.relay]:
58
+ return {'name': name, 'msg': "relay proxy not in relay domain", 'type': 'debug', 'proto': proxy.proto}
59
+
60
+ if domain_db.mode == DomainType.worker and proxy.transport == ProxyTransport.grpc:
61
+ return {'name': name, 'msg': "worker does not support grpc", 'type': 'debug', 'proto': proxy.proto}
62
+
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}
65
+
66
+ if proxy.proto == "trojan" and not is_tls(l3):
67
+ return {'name': name, 'msg': "trojan but not tls", 'type': 'warning', 'proto': proxy.proto}
68
+
69
+ if l3 == "http" and ProxyTransport.XTLS in proxy.transport:
70
+ return {'name': name, 'msg': "http and xtls???", 'type': 'warning', 'proto': proxy.proto}
71
+
72
+ if l3 == "http" and proxy.proto in [ProxyProto.ss, ProxyProto.ssr]:
73
+ return {'name': name, 'msg': "http and ss or ssr???", 'type': 'warning', 'proto': proxy.proto}
74
+
75
+
76
+ def get_port(proxy: Proxy, hconfigs: dict, domain_db: Domain, ptls: int, phttp: int, pport: int | None) -> int:
77
+ l3 = proxy.l3
78
+ port = 443
79
+ if isinstance(phttp, str):
80
+ phttp = int(phttp) if phttp != "None" else None # type: ignore
81
+ if isinstance(ptls, str):
82
+ ptls = int(ptls) if ptls != "None" else None # type: ignore
83
+ if l3 == "kcp":
84
+ port = hconfigs[ConfigEnum.kcp_ports].split(",")[0]
85
+ elif proxy.proto == ProxyProto.wireguard:
86
+ port = hconfigs[ConfigEnum.wireguard_port]
87
+ elif proxy.proto == "tuic":
88
+ port = domain_db.internal_port_tuic
89
+ elif proxy.proto == "hysteria2":
90
+ port = domain_db.internal_port_hysteria2
91
+ elif l3 == 'ssh':
92
+ port = hconfigs[ConfigEnum.ssh_server_port]
93
+ elif is_tls(l3):
94
+ port = ptls
95
+ elif l3 == "http":
96
+ port = phttp
97
+ else:
98
+ port = int(pport) # type: ignore
99
+ return port
100
+
101
+
102
+ def is_tls(l3) -> bool:
103
+ return 'tls' in l3 or "reality" in l3
104
+
105
+
106
+ @cache.cache(ttl=300)
107
+ def get_proxies(child_id: int = 0, only_enabled=False) -> list['Proxy']:
108
+ proxies = Proxy.query.filter(Proxy.child_id == child_id).all()
109
+ proxies = [c for c in proxies if 'restls' not in c.transport]
110
+ # if not hconfig(ConfigEnum.tuic_enable, child_id):
111
+ # proxies = [c for c in proxies if c.proto != ProxyProto.tuic]
112
+ # if not hconfig(ConfigEnum.hysteria_enable, child_id):
113
+ # proxies = [c for c in proxies if c.proto != ProxyProto.hysteria2]
114
+ if not hconfig(ConfigEnum.shadowsocks2022_enable, child_id):
115
+ proxies = [c for c in proxies if 'shadowsocks' != c.transport]
116
+
117
+ if not hconfig(ConfigEnum.ssfaketls_enable, child_id):
118
+ proxies = [c for c in proxies if 'faketls' != c.transport]
119
+ if not hconfig(ConfigEnum.v2ray_enable, child_id):
120
+ proxies = [c for c in proxies if 'v2ray' != c.proto]
121
+ if not hconfig(ConfigEnum.shadowtls_enable, child_id):
122
+ proxies = [c for c in proxies if c.transport != 'shadowtls']
123
+ if not hconfig(ConfigEnum.ssr_enable, child_id):
124
+ proxies = [c for c in proxies if 'ssr' != c.proto]
125
+ if not hconfig(ConfigEnum.vmess_enable, child_id):
126
+ proxies = [c for c in proxies if 'vmess' not in c.proto]
127
+ if not hconfig(ConfigEnum.vless_enable, child_id):
128
+ proxies = [c for c in proxies if 'vless' not in c.proto]
129
+ if not hconfig(ConfigEnum.trojan_enable, child_id):
130
+ proxies = [c for c in proxies if 'trojan' not in c.proto]
131
+ if not hconfig(ConfigEnum.httpupgrade_enable, child_id):
132
+ proxies = [c for c in proxies if ProxyTransport.httpupgrade not in c.transport]
133
+ if not hconfig(ConfigEnum.ws_enable, child_id):
134
+ proxies = [c for c in proxies if ProxyTransport.WS not in c.transport]
135
+ if not hconfig(ConfigEnum.xtls_enable, child_id):
136
+ proxies = [c for c in proxies if ProxyTransport.XTLS not in c.transport]
137
+ if not hconfig(ConfigEnum.grpc_enable, child_id):
138
+ proxies = [c for c in proxies if ProxyTransport.grpc not in c.transport]
139
+ if not hconfig(ConfigEnum.tcp_enable, child_id):
140
+ proxies = [c for c in proxies if 'tcp' not in c.transport]
141
+ if not hconfig(ConfigEnum.h2_enable, child_id):
142
+ proxies = [c for c in proxies if 'h2' not in c.transport and c.l3 not in [ProxyL3.tls_h2_h1, ProxyL3.tls_h2]]
143
+ if not hconfig(ConfigEnum.kcp_enable, child_id):
144
+ proxies = [c for c in proxies if 'kcp' not in c.l3]
145
+ if not hconfig(ConfigEnum.reality_enable, child_id):
146
+ proxies = [c for c in proxies if 'reality' not in c.l3]
147
+ if not hconfig(ConfigEnum.quic_enable, child_id):
148
+ proxies = [c for c in proxies if 'h3_quic' not in c.l3]
149
+ if not hconfig(ConfigEnum.http_proxy_enable, child_id):
150
+ proxies = [c for c in proxies if 'http' != c.l3]
151
+
152
+ if not Domain.query.filter(Domain.mode.in_([DomainType.cdn, DomainType.auto_cdn_ip])).first():
153
+ proxies = [c for c in proxies if c.cdn != "CDN"]
154
+
155
+ if not Domain.query.filter(Domain.mode.in_([DomainType.relay])).first():
156
+ proxies = [c for c in proxies if c.cdn != ProxyCDN.relay]
157
+
158
+ if not Domain.query.filter(Domain.mode.in_([DomainType.cdn, DomainType.auto_cdn_ip]), Domain.servernames != "", Domain.servernames != Domain.domain).first():
159
+ proxies = [c for c in proxies if 'Fake' not in c.cdn]
160
+ proxies = [c for c in proxies if not ('vless' == c.proto and ProxyTransport.tcp == c.transport and c.cdn == ProxyCDN.direct)]
161
+
162
+ if only_enabled:
163
+ proxies = [p for p in proxies if p.enable]
164
+ return proxies
165
+
166
+
167
+ def get_valid_proxies(domains: list[Domain]) -> list[dict]:
168
+ allp = []
169
+ allphttp = [p for p in request.args.get("phttp", "").split(',') if p]
170
+ allptls = [p for p in request.args.get("ptls", "").split(',') if p]
171
+ added_ip = {}
172
+ configsmap = {}
173
+ proxeismap = {}
174
+ for domain in domains:
175
+ if domain.child_id not in configsmap:
176
+ configsmap[domain.child_id] = get_hconfigs(domain.child_id)
177
+ proxeismap[domain.child_id] = get_proxies(domain.child_id, only_enabled=True)
178
+ hconfigs = configsmap[domain.child_id]
179
+
180
+ ip = hutils.network.get_domain_ip(domain.domain, version=4)
181
+ ip6 = hutils.network.get_domain_ip(domain.domain, version=6)
182
+ ips = [x for x in [ip, ip6] if x is not None]
183
+ for proxy in proxeismap[domain.child_id]:
184
+ noDomainProxies = False
185
+ if proxy.proto in [ProxyProto.ssh, ProxyProto.wireguard]:
186
+ noDomainProxies = True
187
+ if proxy.proto in [ProxyProto.ss] and proxy.transport not in [ProxyTransport.grpc, ProxyTransport.h2, ProxyTransport.WS, ProxyTransport.httpupgrade]:
188
+ noDomainProxies = True
189
+ options = []
190
+ key = f'{proxy.proto}{proxy.transport}{proxy.cdn}{proxy.l3}'
191
+ if key not in added_ip:
192
+ added_ip[key] = {}
193
+ if proxy.proto in [ProxyProto.ssh, ProxyProto.tuic, ProxyProto.hysteria2, ProxyProto.wireguard, ProxyProto.ss]:
194
+ if noDomainProxies and all([x in added_ip[key] for x in ips]):
195
+ continue
196
+
197
+ for x in ips:
198
+ added_ip[key][x] = 1
199
+
200
+ if proxy.proto in [ProxyProto.ssh, ProxyProto.wireguard, ProxyProto.ss]:
201
+ if domain.mode == 'fake':
202
+ continue
203
+ if proxy.proto in [ProxyProto.ssh]:
204
+ options = [{'pport': hconfigs[ConfigEnum.ssh_server_port]}]
205
+ elif proxy.proto in [ProxyProto.wireguard]:
206
+ options = [{'pport': hconfigs[ConfigEnum.wireguard_port]}]
207
+ elif proxy.transport in [ProxyTransport.shadowsocks]:
208
+ options = [{'pport': hconfigs[ConfigEnum.shadowsocks2022_port]}]
209
+ elif proxy.proto in [ProxyProto.ss]:
210
+ options = [{'pport': 443}]
211
+ elif proxy.proto == ProxyProto.tuic:
212
+ options = [{'pport': hconfigs[ConfigEnum.tuic_port]}]
213
+ elif proxy.proto == ProxyProto.hysteria2:
214
+ options = [{'pport': hconfigs[ConfigEnum.hysteria_port]}]
215
+ else:
216
+ protos = ['http', 'tls'] if hconfigs.get(ConfigEnum.http_proxy_enable) else ['tls']
217
+ for t in protos:
218
+ for port in hconfigs[ConfigEnum.http_ports if t == 'http' else ConfigEnum.tls_ports].split(','):
219
+ phttp = port if t == 'http' else None
220
+ ptls = port if t == 'tls' else None
221
+ if phttp and len(allphttp) and phttp not in allphttp:
222
+ continue
223
+ if ptls and len(allptls) and ptls not in allptls:
224
+ continue
225
+ options.append({'phttp': phttp, 'ptls': ptls})
226
+
227
+ for opt in options:
228
+ pinfo = make_proxy(hconfigs, proxy, domain, **opt)
229
+ if 'msg' not in pinfo:
230
+ allp.append(pinfo)
231
+ return allp
232
+
233
+
234
+ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=443, pport: int | None = None) -> dict:
235
+
236
+ l3 = proxy.l3
237
+ domain = domain_db.domain
238
+ child_id = domain_db.child_id
239
+ name = proxy.name
240
+ port = hutils.proxy.get_port(proxy, hconfigs, domain_db, ptls, phttp, pport)
241
+
242
+ if val_res := hutils.proxy.is_proxy_valid(proxy, domain_db, port):
243
+ # print(val_res)
244
+ return val_res
245
+
246
+ if 'reality' in proxy.l3:
247
+ alpn = "h2" if proxy.transport in ['h2', "grpc"] else 'http/1.1'
248
+ else:
249
+ alpn = "h2" if proxy.l3 in ['tls_h2'] or proxy.transport in ["grpc", 'h2'] else 'h2,http/1.1' if proxy.l3 == 'tls_h2_h1' else "http/1.1"
250
+ 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))
251
+ is_cdn = ProxyCDN.CDN == proxy.cdn or ProxyCDN.Fake == proxy.cdn
252
+ base = {
253
+ 'name': name,
254
+ 'cdn': is_cdn,
255
+ 'mode': "CDN" if is_cdn else "direct",
256
+ 'l3': l3,
257
+ 'host': domain,
258
+ 'port': port,
259
+ 'server': cdn_forced_host,
260
+ 'sni': domain_db.servernames if is_cdn and domain_db.servernames else domain,
261
+ 'uuid': str(g.account.uuid),
262
+ 'proto': proxy.proto,
263
+ 'transport': proxy.transport,
264
+ 'proxy_path': hconfigs[ConfigEnum.proxy_path],
265
+ 'alpn': alpn,
266
+ 'extra_info': f'{domain_db.alias or domain}',
267
+ 'fingerprint': hconfigs[ConfigEnum.utls],
268
+ 'allow_insecure': domain_db.mode == DomainType.fake or "Fake" in proxy.cdn,
269
+ 'dbe': proxy,
270
+ 'dbdomain': domain_db
271
+ }
272
+ if proxy.proto in ['tuic', 'hysteria2']:
273
+ base['alpn'] = "h3"
274
+ if proxy.proto == 'hysteria2':
275
+ base['hysteria_up_mbps'] = hconfigs.get(ConfigEnum.hysteria_up_mbps)
276
+ base['hysteria_down_mbps'] = hconfigs.get(ConfigEnum.hysteria_down_mbps)
277
+ base['hysteria_obfs_enable'] = hconfigs.get(ConfigEnum.hysteria_obfs_enable)
278
+ base['hysteria_obfs_password'] = hconfigs.get(ConfigEnum.proxy_path) # TODO: it should not be correct
279
+ return base
280
+ if proxy.proto in ['wireguard']:
281
+ base['wg_pub'] = g.account.wg_pub
282
+ base['wg_pk'] = g.account.wg_pk
283
+ base['wg_psk'] = g.account.wg_psk
284
+ base['wg_ipv4'] = hutils.network.add_number_to_ipv4(hconfigs[ConfigEnum.wireguard_ipv4], g.account.id)
285
+ base['wg_ipv6'] = hutils.network.add_number_to_ipv6(hconfigs[ConfigEnum.wireguard_ipv6], g.account.id)
286
+ base['wg_server_pub'] = hconfigs[ConfigEnum.wireguard_public_key]
287
+ base['wg_noise_trick'] = hconfigs[ConfigEnum.wireguard_noise_trick]
288
+ return base
289
+
290
+ if proxy.proto in [ProxyProto.vmess]:
291
+ base['cipher'] = "chacha20-poly1305"
292
+
293
+ if l3 in ['reality']:
294
+ base['reality_short_id'] = random.sample(hconfigs[ConfigEnum.reality_short_ids].split(','), 1)[0]
295
+ # base['flow']="xtls-rprx-vision"
296
+ base['reality_pbk'] = hconfigs[ConfigEnum.reality_public_key]
297
+ if (domain_db.servernames):
298
+ all_servernames = re.split('[ \t\r\n;,]+', domain_db.servernames)
299
+ base['sni'] = random.sample(all_servernames, 1)[0]
300
+ if hconfigs[ConfigEnum.core_type] == "singbox":
301
+ base['sni'] = all_servernames[0]
302
+ else:
303
+ base['sni'] = domain_db.domain
304
+
305
+ del base['host']
306
+ if base.get('fingerprint'):
307
+ base['fingerprint'] = hconfigs[ConfigEnum.utls]
308
+ # if not domain_db.cdn_ip:
309
+ # base['server']=hiddify.get_domain_ip(base['server'])
310
+
311
+ if "Fake" in proxy.cdn:
312
+ if not hconfigs[ConfigEnum.domain_fronting_domain]:
313
+ return {'name': name, 'msg': "no domain_fronting_domain", 'type': 'debug', 'proto': proxy.proto}
314
+ if l3 == "http" and not hconfigs[ConfigEnum.domain_fronting_http_enable]:
315
+ return {'name': name, 'msg': "no http in domain_fronting_domain", 'type': 'debug', 'proto': proxy.proto}
316
+ if l3 == "tls" and not hconfigs[ConfigEnum.domain_fronting_tls_enable]:
317
+ return {'name': name, 'msg': "no tls in domain_fronting_domain", 'type': 'debug', 'proto': proxy.proto}
318
+ base['server'] = hconfigs[ConfigEnum.domain_fronting_domain]
319
+ base['sni'] = hconfigs[ConfigEnum.domain_fronting_domain]
320
+ # base["host"]=domain
321
+ base['mode'] = 'Fake'
322
+ elif l3 == "http" and not hconfigs[ConfigEnum.http_proxy_enable]:
323
+ return {'name': name, 'msg': "http but http is disabled ", 'type': 'debug', 'proto': proxy.proto}
324
+
325
+ path = {
326
+ 'vless': f'{hconfigs[ConfigEnum.path_vless]}',
327
+ 'trojan': f'{hconfigs[ConfigEnum.path_trojan]}',
328
+ 'vmess': f'{hconfigs[ConfigEnum.path_vmess]}',
329
+ 'ss': f'{hconfigs[ConfigEnum.path_ss]}',
330
+ 'v2ray': f'{hconfigs[ConfigEnum.path_ss]}'
331
+ }
332
+
333
+ if base["proto"] in ['v2ray', 'ss', 'ssr']:
334
+ base['cipher'] = hconfigs[ConfigEnum.shadowsocks2022_method]
335
+ base['password'] = f'{hutils.encode.do_base_64(hconfigs[ConfigEnum.shared_secret].replace("-",""))}:{hutils.encode.do_base_64(g.account.uuid.replace("-",""))}'
336
+
337
+ if base['proto'] == 'trojan':
338
+ base['password'] = base['uuid']
339
+ if base["proto"] == "ssr":
340
+ base["ssr-obfs"] = "tls1.2_ticket_auth"
341
+ base["ssr-protocol"] = "auth_sha1_v4"
342
+ base["fakedomain"] = hconfigs[ConfigEnum.ssr_fakedomain]
343
+ base["mode"] = "FakeTLS"
344
+ return base
345
+ elif "faketls" in proxy.transport:
346
+ base['fakedomain'] = hconfigs[ConfigEnum.ssfaketls_fakedomain]
347
+ base['mode'] = 'FakeTLS'
348
+ return base
349
+ elif "shadowtls" in proxy.transport:
350
+ base['fakedomain'] = hconfigs[ConfigEnum.shadowtls_fakedomain]
351
+ # base['sni'] = hconfigs[ConfigEnum.shadowtls_fakedomain]
352
+ base['shared_secret'] = hconfigs[ConfigEnum.shared_secret]
353
+ base['mode'] = 'ShadowTLS'
354
+ return base
355
+ elif "shadowsocks" in proxy.transport:
356
+ return base
357
+ if ProxyTransport.XTLS in proxy.transport:
358
+ base['flow'] = 'xtls-rprx-vision'
359
+ return {**base, 'transport': 'tcp'}
360
+
361
+ if proxy.proto in {'vless', 'trojan', 'vmess'} and hconfigs.get(ConfigEnum.mux_enable):
362
+ if hconfigs[ConfigEnum.mux_enable]:
363
+ base['mux_enable'] = hconfigs[ConfigEnum.core_type]
364
+
365
+ base['mux_protocol'] = hconfigs.get(ConfigEnum.mux_protocol, "h2mux")
366
+ base['mux_max_connections'] = hconfigs.get(ConfigEnum.mux_max_connections, 0)
367
+ base['mux_min_streams'] = hconfigs.get(ConfigEnum.mux_min_streams, 0)
368
+ base['mux_max_streams'] = hconfigs.get(ConfigEnum.mux_max_streams, 0)
369
+ base['mux_padding_enable'] = hconfigs.get(ConfigEnum.mux_padding_enable)
370
+
371
+ if hconfigs[ConfigEnum.mux_brutal_enable]:
372
+ base['mux_brutal_enable'] = True
373
+ base['mux_brutal_up_mbps'] = hconfigs.get(ConfigEnum.mux_brutal_up_mbps, 10)
374
+ base['mux_brutal_down_mbps'] = hconfigs.get(ConfigEnum.mux_brutal_down_mbps, 10)
375
+
376
+ if is_cdn and proxy.proto in {'vless', 'trojan', "vmess"}:
377
+ if hconfigs[ConfigEnum.tls_fragment_enable]:
378
+ base["tls_fragment_enable"] = True
379
+ base["tls_fragment_size"] = hconfigs[ConfigEnum.tls_fragment_size]
380
+ base["tls_fragment_sleep"] = hconfigs[ConfigEnum.tls_fragment_sleep]
381
+
382
+ if hconfigs[ConfigEnum.tls_mixed_case]:
383
+ base["tls_mixed_case"] = hconfigs[ConfigEnum.tls_mixed_case]
384
+ base['host'] = hutils.random.random_case(base['host'])
385
+ base['sni'] = hutils.random.random_case(base['sni'])
386
+ base['server'] = hutils.random.random_case(base['server'])
387
+ if base.get('fakedomain'):
388
+ base['fakedomain'] = hutils.random.random_case(base['fakedomain'])
389
+
390
+ if hconfigs[ConfigEnum.tls_padding_enable]:
391
+ base["tls_padding_enable"] = hconfigs[ConfigEnum.tls_padding_enable]
392
+ base["tls_padding_length"] = hconfigs[ConfigEnum.tls_padding_length]
393
+
394
+ if "tcp" in proxy.transport:
395
+ base['transport'] = 'tcp'
396
+ base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_tcp]}'
397
+ return base
398
+ if proxy.transport in ["ws", "WS"]:
399
+ base['transport'] = 'ws'
400
+ base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_ws]}'
401
+ base["host"] = domain
402
+ return base
403
+
404
+ if proxy.transport in [ProxyTransport.httpupgrade]:
405
+ base['transport'] = 'httpupgrade'
406
+ base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_httpupgrade]}'
407
+ base["host"] = domain
408
+ return base
409
+
410
+ if proxy.transport == "grpc":
411
+ base['transport'] = 'grpc'
412
+ # base['grpc_mode'] = "multi" if hconfigs[ConfigEnum.core_type]=='xray' else 'gun'
413
+ base['grpc_mode'] = 'gun'
414
+ base['grpc_service_name'] = f'{path[base["proto"]]}{hconfigs[ConfigEnum.path_grpc]}'
415
+ base['path'] = base['grpc_service_name']
416
+ return base
417
+
418
+ if "h1" in proxy.transport:
419
+ base['transport'] = 'tcp'
420
+ base['alpn'] = 'http/1.1'
421
+ return base
422
+ if ProxyProto.ssh == proxy.proto:
423
+ base['private_key'] = g.account.ed25519_private_key
424
+ base['host_key'] = hutils.proxy.get_ssh_hostkeys(False)
425
+ # base['ssh_port'] = hconfig(ConfigEnum.ssh_server_port)
426
+ return base
427
+ return {'name': name, 'msg': 'not valid', 'type': 'error', 'proto': proxy.proto}
428
+
429
+
430
+ class ProxyJsonEncoder(json.JSONEncoder):
431
+ def default(self, obj):
432
+ if isinstance(obj, IPv4Address) or isinstance(obj, IPv6Address):
433
+ return str(obj)
434
+ return super().default(obj)