hiddifypanel 9.0.0.dev90__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.
- hiddifypanel/VERSION +1 -1
- hiddifypanel/VERSION.py +2 -2
- hiddifypanel/auth.py +30 -9
- hiddifypanel/base.py +60 -52
- hiddifypanel/cache.py +43 -25
- hiddifypanel/database.py +9 -0
- hiddifypanel/drivers/abstract_driver.py +2 -0
- hiddifypanel/drivers/singbox_api.py +17 -15
- hiddifypanel/drivers/ssh_liberty_bridge_api.py +3 -1
- hiddifypanel/drivers/user_driver.py +12 -6
- hiddifypanel/drivers/wireguard_api.py +7 -2
- hiddifypanel/drivers/xray_api.py +14 -9
- hiddifypanel/hutils/__init__.py +4 -0
- hiddifypanel/hutils/convert.py +13 -2
- hiddifypanel/hutils/crypto.py +48 -0
- hiddifypanel/hutils/encode.py +4 -1
- hiddifypanel/hutils/flask.py +38 -5
- hiddifypanel/hutils/github_issue.py +1 -1
- hiddifypanel/hutils/importer/xui.py +5 -2
- hiddifypanel/{models/utils.py → hutils/model.py} +14 -4
- hiddifypanel/hutils/network/auto_ip_selector.py +2 -0
- hiddifypanel/hutils/network/net.py +46 -2
- hiddifypanel/hutils/node/__init__.py +3 -0
- hiddifypanel/hutils/node/api_client.py +76 -0
- hiddifypanel/hutils/node/child.py +147 -0
- hiddifypanel/hutils/node/parent.py +100 -0
- hiddifypanel/hutils/node/shared.py +65 -0
- hiddifypanel/hutils/proxy/__init__.py +5 -0
- hiddifypanel/hutils/proxy/clash.py +161 -0
- hiddifypanel/hutils/proxy/shared.py +434 -0
- hiddifypanel/hutils/proxy/singbox.py +339 -0
- hiddifypanel/hutils/proxy/xray.py +235 -0
- hiddifypanel/hutils/proxy/xrayjson.py +391 -0
- hiddifypanel/hutils/random.py +4 -0
- hiddifypanel/hutils/utils.py +4 -1
- hiddifypanel/models/__init__.py +2 -2
- hiddifypanel/models/admin.py +31 -17
- hiddifypanel/models/base_account.py +7 -7
- hiddifypanel/models/child.py +30 -16
- hiddifypanel/models/config.py +45 -16
- hiddifypanel/models/config_enum.py +68 -17
- hiddifypanel/models/domain.py +28 -20
- hiddifypanel/models/parent_domain.py +2 -2
- hiddifypanel/models/proxy.py +29 -20
- hiddifypanel/models/report.py +2 -3
- hiddifypanel/models/usage.py +2 -2
- hiddifypanel/models/user.py +33 -22
- hiddifypanel/panel/admin/Actions.py +13 -19
- hiddifypanel/panel/admin/AdminstratorAdmin.py +14 -3
- hiddifypanel/panel/admin/Dashboard.py +5 -10
- hiddifypanel/panel/admin/DomainAdmin.py +35 -48
- hiddifypanel/panel/admin/NodeAdmin.py +6 -2
- hiddifypanel/panel/admin/ProxyAdmin.py +6 -5
- hiddifypanel/panel/admin/QuickSetup.py +21 -20
- hiddifypanel/panel/admin/SettingAdmin.py +107 -62
- hiddifypanel/panel/admin/UserAdmin.py +22 -21
- hiddifypanel/panel/admin/templates/index.html +1 -1
- hiddifypanel/panel/admin/templates/model/user_list.html +44 -20
- hiddifypanel/panel/admin/templates/parent_dash.html +2 -4
- hiddifypanel/panel/admin/templates/result.html +2 -3
- hiddifypanel/panel/cf_api.py +1 -2
- hiddifypanel/panel/cli.py +16 -16
- hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +16 -12
- hiddifypanel/panel/commercial/__init__.py +7 -5
- hiddifypanel/panel/commercial/restapi/v1/__init__.py +1 -1
- hiddifypanel/panel/commercial/restapi/v1/tgbot.py +1 -1
- hiddifypanel/panel/commercial/restapi/v1/tgmsg.py +14 -10
- hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +0 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +2 -2
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_log_api.py +4 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +8 -25
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +4 -4
- hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +157 -0
- hiddifypanel/panel/commercial/restapi/v2/admin/server_status_api.py +3 -3
- hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +9 -66
- hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -1
- hiddifypanel/panel/commercial/restapi/v2/child/__init__.py +18 -0
- hiddifypanel/panel/commercial/restapi/v2/child/actions.py +63 -0
- hiddifypanel/panel/commercial/restapi/v2/child/register_parent_api.py +34 -0
- hiddifypanel/panel/commercial/restapi/v2/child/schema.py +7 -0
- hiddifypanel/panel/commercial/restapi/v2/child/sync_parent_api.py +21 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/__init__.py +13 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/info.py +18 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/ping_pong.py +23 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/schema.py +7 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/__init__.py +16 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +65 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +115 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/status_api.py +26 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +53 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/usage_api.py +57 -0
- hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +17 -23
- hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +23 -26
- hiddifypanel/panel/commercial/telegrambot/admin.py +1 -2
- hiddifypanel/panel/common.py +25 -8
- hiddifypanel/panel/common_bp/login.py +2 -2
- hiddifypanel/panel/hiddify.py +22 -185
- hiddifypanel/panel/init_db.py +102 -55
- hiddifypanel/panel/usage.py +33 -18
- hiddifypanel/panel/user/__init__.py +0 -1
- hiddifypanel/panel/user/templates/all_configs copy.txt +2 -2
- hiddifypanel/panel/user/templates/all_configs.txt +2 -2
- hiddifypanel/panel/user/templates/base_singbox_config.json.j2 +2 -1
- hiddifypanel/panel/user/templates/base_xray_config.json.j2 +125 -0
- hiddifypanel/panel/user/templates/clash_config copy.yml +1 -1
- hiddifypanel/panel/user/templates/clash_config.yml +4 -4
- hiddifypanel/panel/user/templates/clash_proxies.yml +1 -1
- hiddifypanel/panel/user/templates/home/all-configs.html +2 -2
- hiddifypanel/panel/user/templates/home/all-configs_old.html +1 -1
- hiddifypanel/panel/user/templates/home/ios copy.html +2 -2
- hiddifypanel/panel/user/templates/home/usage.html +1 -1
- hiddifypanel/panel/user/templates/new.html +2 -2
- hiddifypanel/panel/user/user.py +56 -50
- hiddifypanel/static/css/custom.css +31 -0
- hiddifypanel/static/images/favicon.ico +0 -0
- hiddifypanel/static/images/hiddify-old.png +0 -0
- hiddifypanel/static/images/hiddify.png +0 -0
- hiddifypanel/static/images/hiddify2.png +0 -0
- hiddifypanel/static/new/assets/{index-1b891a7c.js → index-ccb9873c.js} +56 -56
- hiddifypanel/static/new/assets/index-fa00de9a.css +1 -0
- hiddifypanel/static/new/i18n/en.json +6 -6
- hiddifypanel/static/new/i18n/fa.json +2 -2
- hiddifypanel/templates/admin-layout.html +30 -43
- hiddifypanel/templates/fake.html +0 -4
- hiddifypanel/templates/flaskadmin-layout.html +7 -3
- hiddifypanel/templates/master.html +11 -6
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +2082 -1977
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +2035 -1924
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +1911 -1840
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +2036 -1881
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +1857 -1720
- hiddifypanel/translations.i18n/en.json +992 -933
- hiddifypanel/translations.i18n/fa.json +994 -935
- hiddifypanel/translations.i18n/pt.json +994 -935
- hiddifypanel/translations.i18n/ru.json +994 -935
- hiddifypanel/translations.i18n/zh.json +971 -912
- {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/METADATA +47 -47
- {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/RECORD +147 -120
- {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/WHEEL +1 -1
- hiddifypanel/panel/commercial/restapi/v2/DTO.py +0 -9
- hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py +0 -16
- hiddifypanel/panel/commercial/restapi/v2/hello/hello.py +0 -32
- hiddifypanel/panel/user/link_maker.py +0 -1083
- hiddifypanel/static/new/assets/index-669b32c8.css +0 -1
- {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,391 @@
|
|
1
|
+
import datetime
|
2
|
+
import json
|
3
|
+
import copy
|
4
|
+
from flask import render_template, request, g
|
5
|
+
from hiddifypanel import hutils
|
6
|
+
from hiddifypanel.models import Proxy, ProxyTransport, ProxyL3, ProxyCDN, ProxyProto, Domain, ConfigEnum, DomainType
|
7
|
+
from flask_babel import gettext as _
|
8
|
+
from .xray import is_muxable_agent
|
9
|
+
OUTBOUND_LEVEL = 8
|
10
|
+
|
11
|
+
|
12
|
+
def configs_as_json(domains: list[Domain], remarks: str) -> str:
|
13
|
+
'''Returns xray configs as json'''
|
14
|
+
outbounds = []
|
15
|
+
for proxy in hutils.proxy.get_valid_proxies(domains):
|
16
|
+
outbound = to_xray(proxy)
|
17
|
+
if 'msg' not in outbound:
|
18
|
+
outbounds.append(outbound)
|
19
|
+
|
20
|
+
outbounds_len = len(outbounds)
|
21
|
+
# reutrn no outbound
|
22
|
+
if outbounds_len < 1:
|
23
|
+
return ''
|
24
|
+
|
25
|
+
all_configs = []
|
26
|
+
base_config = json.loads(render_template('base_xray_config.json.j2', remarks=remarks))
|
27
|
+
# multiple outbounds needs multiple whole base config not just one with multiple outbounds (at least for v2rayng)
|
28
|
+
# https://github.com/2dust/v2rayNG/pull/2827#issue-2127534078
|
29
|
+
if outbounds_len > 1:
|
30
|
+
for out in outbounds:
|
31
|
+
base_config['remarks'] = out['tag']
|
32
|
+
base_config['outbounds'].insert(0, out)
|
33
|
+
all_configs.append(copy.deepcopy(base_config))
|
34
|
+
del base_config['outbounds'][0]
|
35
|
+
else: # single outbound
|
36
|
+
base_config['outbounds'].insert(0, outbounds[0])
|
37
|
+
all_configs = base_config
|
38
|
+
|
39
|
+
json_configs = json.dumps(all_configs, indent=2, cls=hutils.proxy.ProxyJsonEncoder)
|
40
|
+
return json_configs
|
41
|
+
|
42
|
+
|
43
|
+
def to_xray(proxy: dict) -> dict:
|
44
|
+
outbound = {
|
45
|
+
'tag': f'{proxy["extra_info"]} {proxy["name"]} § {proxy["port"]} {proxy["dbdomain"].id}',
|
46
|
+
'protocol': str(proxy['proto']),
|
47
|
+
'settings': {},
|
48
|
+
'streamSettings': {},
|
49
|
+
'mux': { # default value
|
50
|
+
'enabled': False,
|
51
|
+
'concurrency': -1
|
52
|
+
}
|
53
|
+
}
|
54
|
+
# add multiplex to outbound
|
55
|
+
add_multiplex(outbound, proxy)
|
56
|
+
|
57
|
+
# add stream setting to outbound
|
58
|
+
add_stream_settings(outbound, proxy)
|
59
|
+
|
60
|
+
# add protocol settings to outbound
|
61
|
+
add_proto_settings(outbound, proxy)
|
62
|
+
|
63
|
+
return outbound
|
64
|
+
|
65
|
+
# region proto settings
|
66
|
+
|
67
|
+
|
68
|
+
def add_proto_settings(base: dict, proxy: dict):
|
69
|
+
if proxy['proto'] == ProxyProto.wireguard:
|
70
|
+
add_wireguard_settings(base, proxy)
|
71
|
+
elif proxy['proto'] == ProxyProto.ss:
|
72
|
+
add_shadowsocks_settings(base, proxy)
|
73
|
+
elif proxy['proto'] == ProxyProto.vless:
|
74
|
+
add_vless_settings(base, proxy)
|
75
|
+
elif proxy['proto'] == ProxyProto.vmess:
|
76
|
+
add_vmess_settings(base, proxy)
|
77
|
+
elif proxy['proto'] == ProxyProto.trojan:
|
78
|
+
proxy['password'] = proxy['uuid']
|
79
|
+
add_trojan_settings(base, proxy)
|
80
|
+
|
81
|
+
|
82
|
+
def add_wireguard_settings(base: dict, proxy: dict):
|
83
|
+
|
84
|
+
base['settings']['secretKey'] = proxy['wg_pk']
|
85
|
+
base['settings']['reversed'] = [0, 0, 0]
|
86
|
+
base['settings']['mtu'] = 1380 # optional
|
87
|
+
base['settings']['peers'] = [{
|
88
|
+
'endpoint': f'{proxy["server"]}:{int(proxy["port"])}',
|
89
|
+
'publicKey': proxy["wg_server_pub"]
|
90
|
+
# 'allowedIPs':'', 'preSharedKey':'', 'keepAlive':'' # optionals
|
91
|
+
}]
|
92
|
+
|
93
|
+
# optionals
|
94
|
+
# base['settings']['address'] = [f'{proxy["wg_ipv4"]}/32',f'{proxy["wg_ipv6"]}/128']
|
95
|
+
# base['settings']['workers'] = 4
|
96
|
+
# base['settings']['domainStrategy'] = 'ForceIP' # default
|
97
|
+
|
98
|
+
|
99
|
+
def add_vless_settings(base: dict, proxy: dict):
|
100
|
+
base['settings']['vnext'] = [
|
101
|
+
{
|
102
|
+
'address': proxy['server'],
|
103
|
+
'port': proxy['port'],
|
104
|
+
"users": [
|
105
|
+
{
|
106
|
+
'id': proxy['uuid'],
|
107
|
+
'encryption': 'none',
|
108
|
+
# 'security': 'auto',
|
109
|
+
'flow': 'xtls-rprx-vision' if (proxy['transport'] == ProxyTransport.XTLS or base['streamSettings']['security'] == 'reality') else '',
|
110
|
+
'level': OUTBOUND_LEVEL
|
111
|
+
}
|
112
|
+
]
|
113
|
+
}
|
114
|
+
]
|
115
|
+
|
116
|
+
|
117
|
+
def add_vmess_settings(base: dict, proxy: dict):
|
118
|
+
base['settings']['vnext'] = [
|
119
|
+
{
|
120
|
+
"address": proxy['server'],
|
121
|
+
"port": proxy['port'],
|
122
|
+
"users": [
|
123
|
+
{
|
124
|
+
"id": proxy['uuid'],
|
125
|
+
"security": proxy['cipher'],
|
126
|
+
"level": OUTBOUND_LEVEL
|
127
|
+
}
|
128
|
+
]
|
129
|
+
}
|
130
|
+
]
|
131
|
+
|
132
|
+
|
133
|
+
def add_trojan_settings(base: dict, proxy: dict):
|
134
|
+
base['settings']['servers'] = [
|
135
|
+
{
|
136
|
+
# 'email': proxy['uuid'], optional
|
137
|
+
'address': proxy['server'],
|
138
|
+
'port': proxy['port'],
|
139
|
+
'password': proxy['password'],
|
140
|
+
'level': OUTBOUND_LEVEL
|
141
|
+
}
|
142
|
+
]
|
143
|
+
|
144
|
+
|
145
|
+
def add_shadowsocks_settings(base: dict, proxy: dict):
|
146
|
+
base['settings']['servers'] = [
|
147
|
+
{
|
148
|
+
'address': proxy['server'],
|
149
|
+
'port': proxy['port'],
|
150
|
+
'method': proxy['cipher'],
|
151
|
+
'password': proxy['password'],
|
152
|
+
'uot': True,
|
153
|
+
'level': OUTBOUND_LEVEL
|
154
|
+
# 'email': '', optional
|
155
|
+
}
|
156
|
+
]
|
157
|
+
|
158
|
+
# endregion
|
159
|
+
|
160
|
+
|
161
|
+
# region stream settings
|
162
|
+
|
163
|
+
def add_stream_settings(base: dict, proxy: dict):
|
164
|
+
ss = base['streamSettings']
|
165
|
+
ss['security'] = 'none' # default
|
166
|
+
|
167
|
+
# security
|
168
|
+
if proxy['l3'] == ProxyL3.reality:
|
169
|
+
ss['security'] = 'reality'
|
170
|
+
elif proxy['l3'] in [ProxyL3.tls, ProxyL3.tls_h2, ProxyL3.tls_h2_h1]:
|
171
|
+
ss['security'] = 'tls'
|
172
|
+
|
173
|
+
# network and transport settings
|
174
|
+
if ss['security'] == 'tls' or 'xtls':
|
175
|
+
ss['tlsSettings'] = {
|
176
|
+
'serverName': proxy['sni'],
|
177
|
+
'allowInsecure': proxy['allow_insecure'],
|
178
|
+
'fingerprint': proxy['fingerprint'],
|
179
|
+
'alpn': [proxy['alpn']],
|
180
|
+
# 'minVersion': '1.2',
|
181
|
+
# 'disableSystemRoot': '',
|
182
|
+
# 'enableSessionResumption': '',
|
183
|
+
# 'pinnedPeerCertificateChainSha256': '',
|
184
|
+
# 'certificates': '',
|
185
|
+
# 'maxVersion': '1.3', # Go lang sets
|
186
|
+
# 'cipherSuites': '', # Go lang sets
|
187
|
+
# 'rejectUnknownSni': '', # default is false
|
188
|
+
}
|
189
|
+
if ss['security'] == 'reality':
|
190
|
+
ss['network'] = proxy['transport']
|
191
|
+
add_reality_stream(ss, proxy)
|
192
|
+
if proxy['l3'] == ProxyL3.kcp:
|
193
|
+
ss['network'] = 'kcp'
|
194
|
+
add_kcp_stream(ss, proxy)
|
195
|
+
|
196
|
+
if proxy['l3'] == ProxyL3.h3_quic:
|
197
|
+
add_quic_stream(ss, proxy)
|
198
|
+
|
199
|
+
if proxy['transport'] == 'tcp' or ss['security'] == 'reality' or (ss['security'] == 'none' and proxy['transport'] not in [ProxyTransport.httpupgrade, ProxyTransport.WS]):
|
200
|
+
ss['network'] = proxy['transport']
|
201
|
+
add_tcp_stream(ss, proxy)
|
202
|
+
if proxy['transport'] == ProxyTransport.h2 and ss['security'] == 'none' and ss['security'] != 'reality':
|
203
|
+
ss['network'] = proxy['transport']
|
204
|
+
add_http_stream(ss, proxy)
|
205
|
+
if proxy['transport'] == ProxyTransport.grpc:
|
206
|
+
ss['network'] = proxy['transport']
|
207
|
+
add_grpc_stream(ss, proxy)
|
208
|
+
if proxy['transport'] == ProxyTransport.httpupgrade:
|
209
|
+
ss['network'] = proxy['transport']
|
210
|
+
add_httpupgrade_stream(ss, proxy)
|
211
|
+
if proxy['transport'] == 'ws':
|
212
|
+
ss['network'] = proxy['transport']
|
213
|
+
add_ws_stream(ss, proxy)
|
214
|
+
|
215
|
+
# tls fragmentaion
|
216
|
+
add_tls_fragmentation_stream_settings(base, proxy)
|
217
|
+
|
218
|
+
|
219
|
+
def add_tcp_stream(ss: dict, proxy: dict):
|
220
|
+
if proxy['l3'] == ProxyL3.http:
|
221
|
+
ss['tcpSettings'] = {
|
222
|
+
'header': {
|
223
|
+
'type': 'http',
|
224
|
+
'request': {
|
225
|
+
'path': [proxy['path']]
|
226
|
+
}
|
227
|
+
}
|
228
|
+
# 'acceptProxyProtocol': False
|
229
|
+
}
|
230
|
+
else:
|
231
|
+
ss['tcpSettings'] = {
|
232
|
+
'header': {
|
233
|
+
'type': 'none'
|
234
|
+
}
|
235
|
+
# 'acceptProxyProtocol': False
|
236
|
+
}
|
237
|
+
|
238
|
+
|
239
|
+
def add_http_stream(ss: dict, proxy: dict):
|
240
|
+
ss['httpSettings'] = {
|
241
|
+
'host': proxy['host'],
|
242
|
+
'path': proxy['path'],
|
243
|
+
# 'read_idle_timeout': 10, # default disabled
|
244
|
+
# 'health_check_timeout': 15, # default is 15
|
245
|
+
# 'method': 'PUT', # default is 15
|
246
|
+
# 'headers': {
|
247
|
+
|
248
|
+
# }
|
249
|
+
}
|
250
|
+
|
251
|
+
|
252
|
+
def add_ws_stream(ss: dict, proxy: dict):
|
253
|
+
ss['wsSettings'] = {
|
254
|
+
'path': proxy['path'],
|
255
|
+
'headers': {
|
256
|
+
"Host": proxy['host']
|
257
|
+
}
|
258
|
+
# 'acceptProxyProtocol': False,
|
259
|
+
}
|
260
|
+
|
261
|
+
|
262
|
+
def add_grpc_stream(ss: dict, proxy: dict):
|
263
|
+
ss['grpcSettings'] = {
|
264
|
+
'serviceName': proxy['path'], # proxy['path'] is equal toproxy['grpc_service_name']
|
265
|
+
'idle_timeout': 115, # by default, the health check is not enabled. may solve some "connection drop" issues
|
266
|
+
'health_check_timeout': 20, # default is 20
|
267
|
+
# 'initial_windows_size': 0, # 0 means disabled. greater than 65535 means Dynamic Window mechanism will be disabled
|
268
|
+
# 'permit_without_stream': False, # health check performed when there are no sub-connections
|
269
|
+
# 'multiMode': false, # experimental
|
270
|
+
}
|
271
|
+
|
272
|
+
|
273
|
+
def add_httpupgrade_stream(ss: dict, proxy: dict):
|
274
|
+
ss['httpupgradeSettings'] = {
|
275
|
+
'path': proxy['path'],
|
276
|
+
'host': proxy['host'],
|
277
|
+
# 'acceptProxyProtocol': '', for inbounds only
|
278
|
+
}
|
279
|
+
|
280
|
+
|
281
|
+
def add_kcp_stream(ss: dict, proxy: dict):
|
282
|
+
# TODO: fix server side configs first
|
283
|
+
ss['kcpSettings'] = {}
|
284
|
+
return
|
285
|
+
ss['kcpSettings'] = {
|
286
|
+
'seed': proxy['path'],
|
287
|
+
# 'mtu': 1350, # optional, default value is written
|
288
|
+
# 'tti': 50, # optional, default value is written
|
289
|
+
# 'uplinkCapacity': 5, # optional, default value is written
|
290
|
+
# 'downlinkCapacity': 20, # optional, default value is written
|
291
|
+
# 'congestion':False, # optional, default value is written
|
292
|
+
# 'readBufferSize': 2,# optional, default value is written
|
293
|
+
# 'writeBufferSize':2 # optional, default value is written
|
294
|
+
# 'header': { # must be same as server (hiddify doesn't use yet)
|
295
|
+
# 'type': 'none' # choices: none(default), srtp, utp, wechat-video, dtls, wireguards
|
296
|
+
# }
|
297
|
+
}
|
298
|
+
|
299
|
+
|
300
|
+
def add_quic_stream(ss: dict, proxy: dict):
|
301
|
+
# TODO: fix server side configs first
|
302
|
+
ss['quicSettings'] = {}
|
303
|
+
return
|
304
|
+
ss['quicSettings'] = {
|
305
|
+
'security': 'chacha20-poly1305',
|
306
|
+
'key': proxy['path'],
|
307
|
+
'header': {
|
308
|
+
'type': 'none'
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
|
313
|
+
def add_reality_stream(ss: dict, proxy: dict):
|
314
|
+
ss['realitySettings'] = {
|
315
|
+
'serverName': proxy['sni'],
|
316
|
+
'fingerprint': proxy['fingerprint'],
|
317
|
+
'shortId': proxy['reality_short_id'],
|
318
|
+
'publicKey': proxy['reality_pbk'],
|
319
|
+
'show': False,
|
320
|
+
}
|
321
|
+
|
322
|
+
|
323
|
+
def add_tls_fragmentation_stream_settings(base: dict, proxy: dict):
|
324
|
+
'''Adds tls fragment in the outbounds if tls fragmentation is enabled'''
|
325
|
+
if base['streamSettings']['security'] in ['tls', 'reality']:
|
326
|
+
if proxy.get('tls_fragment_enable'):
|
327
|
+
base['streamSettings']['sockopt'] = {
|
328
|
+
'dialerProxy': 'fragment',
|
329
|
+
'tcpKeepAliveIdle': 100,
|
330
|
+
'tcpNoDelay': True, # recommended to be enabled with "tcpMptcp": true.
|
331
|
+
"mark": 255
|
332
|
+
# 'tcpFastOpen': True, # the system default setting be used.
|
333
|
+
# 'tcpKeepAliveInterval': 0, # 0 means default GO lang settings, -1 means not enable
|
334
|
+
# 'tcpcongestion': bbr, # Not configuring means using the system default value
|
335
|
+
# 'tcpMptcp': True, # need to be enabled in both server and client configuration (not supported by panel yet)
|
336
|
+
}
|
337
|
+
|
338
|
+
# endregion
|
339
|
+
|
340
|
+
|
341
|
+
def add_multiplex(base: dict, proxy: dict):
|
342
|
+
if proxy.get('mux_enable') != "xray":
|
343
|
+
return
|
344
|
+
|
345
|
+
concurrency = proxy['mux_max_connections']
|
346
|
+
if concurrency and concurrency > 0:
|
347
|
+
base['mux']['enabled'] = True
|
348
|
+
base['mux']['concurrency'] = concurrency
|
349
|
+
base['mux']['xudpConcurrency'] = concurrency
|
350
|
+
base['mux']['xudpProxyUDP443'] = 'reject'
|
351
|
+
|
352
|
+
|
353
|
+
def add_tls_tricks_to_dict(d: dict, proxy: dict):
|
354
|
+
if proxy.get('tls_fragment_enable'):
|
355
|
+
if g.user_agent.get('is_shadowrocket'):
|
356
|
+
d['fragment'] = f'1,{proxy["tls_fragment_size"]},{proxy["tls_fragment_sleep"]}'
|
357
|
+
else:
|
358
|
+
d['fragment'] = f'{proxy["tls_fragment_size"]},{proxy["tls_fragment_sleep"]},tlshello'
|
359
|
+
|
360
|
+
if proxy.get("tls_mixed_case"):
|
361
|
+
d['mc'] = 1
|
362
|
+
if proxy.get("tls_padding_enable"):
|
363
|
+
d['padsize'] = proxy["tls_padding_length"]
|
364
|
+
|
365
|
+
|
366
|
+
def add_mux_to_dict(d: dict, proxy):
|
367
|
+
if not is_muxable_agent(proxy):
|
368
|
+
return
|
369
|
+
|
370
|
+
# according to github.com/hiddify/ray2sing/
|
371
|
+
d['muxtype'] = proxy["mux_protocol"]
|
372
|
+
d['muxmaxc'] = proxy["mux_max_connections"]
|
373
|
+
d['mux'] = proxy['mux_min_streams']
|
374
|
+
d['muxsmax'] = proxy["mux_max_streams"]
|
375
|
+
d['muxpad'] = proxy["mux_padding_enable"]
|
376
|
+
|
377
|
+
if proxy.get('mux_brutal_enable'):
|
378
|
+
d['muxup'] = proxy["mux_brutal_up_mbps"]
|
379
|
+
d['muxdown'] = proxy["mux_brutal_down_mbps"]
|
380
|
+
|
381
|
+
|
382
|
+
def add_tls_tricks_to_link(proxy: dict) -> str:
|
383
|
+
out = {}
|
384
|
+
add_tls_tricks_to_dict(out, proxy)
|
385
|
+
return hutils.encode.convert_dict_to_url(out)
|
386
|
+
|
387
|
+
|
388
|
+
def add_mux_to_link(proxy: dict) -> str:
|
389
|
+
out = {}
|
390
|
+
add_mux_to_dict(out, proxy)
|
391
|
+
return hutils.encode.convert_dict_to_url(out)
|
hiddifypanel/hutils/random.py
CHANGED
hiddifypanel/hutils/utils.py
CHANGED
@@ -31,7 +31,10 @@ def get_latest_release_version(repo_name):
|
|
31
31
|
if location_header:
|
32
32
|
version = re.search(r"/([^/]+)/?$", location_header)
|
33
33
|
if version:
|
34
|
-
|
34
|
+
ver = version.group(1).replace('v', '')
|
35
|
+
if ver == "latest":
|
36
|
+
return get_latest_release_version(repo_name.replace("-", ""))
|
37
|
+
return ver
|
35
38
|
except Exception as e:
|
36
39
|
return f'{e}'
|
37
40
|
|
hiddifypanel/models/__init__.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
from .role import Role, AccountType
|
2
2
|
from .child import Child, ChildMode
|
3
|
-
from .config_enum import ConfigCategory, ConfigEnum, Lang, ApplyMode
|
3
|
+
from .config_enum import ConfigCategory, ConfigEnum, Lang, ApplyMode, PanelMode, LogLevel
|
4
4
|
from .config import StrConfig, BoolConfig, get_hconfigs, hconfig, set_hconfig, add_or_update_config, bulk_register_configs, get_hconfigs_childs
|
5
5
|
|
6
6
|
# from .parent_domain import ParentDomain
|
7
|
-
from .domain import Domain, DomainType, ShowDomain, get_domain, get_current_proxy_domains, get_panel_domains, get_proxy_domains, get_proxy_domains_db, get_hdomains, hdomain, add_or_update_domain, bulk_register_domains
|
7
|
+
from .domain import Domain, DomainType, ShowDomain, get_domain, get_current_proxy_domains, get_panel_domains, get_proxy_domains, get_proxy_domains_db, get_hdomains, hdomain, add_or_update_domain, bulk_register_domains, get_panel_link
|
8
8
|
from .proxy import Proxy, ProxyL3, ProxyCDN, ProxyProto, ProxyTransport
|
9
9
|
from .user import User, UserMode, UserDetail, ONE_GIG
|
10
10
|
from .admin import AdminUser, AdminMode
|
hiddifypanel/models/admin.py
CHANGED
@@ -2,13 +2,12 @@ from enum import auto
|
|
2
2
|
import uuid
|
3
3
|
from flask import g
|
4
4
|
from hiddifypanel.models.usage import DailyUsage
|
5
|
-
from
|
6
|
-
from sqlalchemy import event
|
5
|
+
from sqlalchemy import event, Column, Integer, Enum, Boolean, ForeignKey
|
7
6
|
from strenum import StrEnum
|
8
7
|
from apiflask import abort
|
9
8
|
from flask_babel import gettext as __
|
10
9
|
from flask_babel import lazy_gettext as _
|
11
|
-
from hiddifypanel.database import db
|
10
|
+
from hiddifypanel.database import db, db_execute
|
12
11
|
from hiddifypanel.models.role import Role
|
13
12
|
from hiddifypanel.models.base_account import BaseAccount
|
14
13
|
|
@@ -30,15 +29,15 @@ class AdminUser(BaseAccount):
|
|
30
29
|
This is a model class for a user in a database that includes columns for their ID, UUID, name, online status,
|
31
30
|
account expiration date, usage limit, package days, mode, start date, current usage, last reset time, and comment.
|
32
31
|
"""
|
33
|
-
id =
|
34
|
-
mode =
|
35
|
-
can_add_admin =
|
36
|
-
max_users =
|
37
|
-
max_active_users =
|
38
|
-
users = db.relationship('User', backref='admin')
|
39
|
-
usages = db.relationship('DailyUsage', backref='admin')
|
40
|
-
parent_admin_id =
|
41
|
-
parent_admin = db.relationship('AdminUser', remote_side=[id], backref='sub_admins')
|
32
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
33
|
+
mode = Column(Enum(AdminMode), default=AdminMode.agent, nullable=False)
|
34
|
+
can_add_admin = Column(Boolean, default=False, nullable=False)
|
35
|
+
max_users = Column(Integer, default=100, nullable=False)
|
36
|
+
max_active_users = Column(Integer, default=100, nullable=False)
|
37
|
+
users = db.relationship('User', backref='admin') # type: ignore
|
38
|
+
usages = db.relationship('DailyUsage', backref='admin') # type: ignore
|
39
|
+
parent_admin_id = Column(Integer, ForeignKey('admin_user.id'), default=1)
|
40
|
+
parent_admin = db.relationship('AdminUser', remote_side=[id], backref='sub_admins') # type: ignore
|
42
41
|
|
43
42
|
@property
|
44
43
|
def role(self) -> Role | None:
|
@@ -51,15 +50,28 @@ class AdminUser(BaseAccount):
|
|
51
50
|
return Role.agent
|
52
51
|
return None
|
53
52
|
|
53
|
+
@staticmethod
|
54
|
+
def form_schema(schema):
|
55
|
+
return schema.dump(AdminUser())
|
56
|
+
|
57
|
+
def to_schema(self):
|
58
|
+
admin_dict = self.to_dict()
|
59
|
+
from hiddifypanel.panel.commercial.restapi.v2.admin.admin_user_api import AdminSchema
|
60
|
+
return AdminSchema().load(admin_dict)
|
61
|
+
|
54
62
|
def get_id(self) -> str | None:
|
55
63
|
return f'admin_{self.id}'
|
56
64
|
|
57
|
-
def to_dict(self, convert_date=True) -> dict:
|
65
|
+
def to_dict(self, convert_date=True, dump_id=False) -> dict:
|
58
66
|
base = super().to_dict()
|
67
|
+
if dump_id:
|
68
|
+
base['id'] = self.id
|
69
|
+
from hiddifypanel.models import hconfig, ConfigEnum
|
59
70
|
return {**base,
|
60
71
|
'mode': self.mode,
|
61
72
|
'can_add_admin': self.can_add_admin,
|
62
73
|
'parent_admin_uuid': self.parent_admin.uuid if self.parent_admin else None,
|
74
|
+
'lang': hconfig(ConfigEnum.admin_lang)
|
63
75
|
}
|
64
76
|
|
65
77
|
@classmethod
|
@@ -87,7 +99,7 @@ class AdminUser(BaseAccount):
|
|
87
99
|
dbuser.parent_admin_id = parent_admin.id # type: ignore
|
88
100
|
|
89
101
|
dbuser.mode = data.get('mode', AdminMode.agent)
|
90
|
-
dbuser.can_add_admin = data.get('can_add_admin')
|
102
|
+
dbuser.can_add_admin = data.get('can_add_admin') or False
|
91
103
|
if commit:
|
92
104
|
db.session.commit()
|
93
105
|
return dbuser
|
@@ -143,7 +155,8 @@ class AdminUser(BaseAccount):
|
|
143
155
|
if not admin:
|
144
156
|
db.session.add(AdminUser(id=1, uuid=str(uuid.uuid4()), name="Owner", mode=AdminMode.super_admin, comment=""))
|
145
157
|
db.session.commit()
|
146
|
-
|
158
|
+
|
159
|
+
db_execute("update admin_user set id=1 where name='Owner'")
|
147
160
|
admin = AdminUser.by_id(1)
|
148
161
|
|
149
162
|
return admin
|
@@ -161,5 +174,6 @@ class AdminUser(BaseAccount):
|
|
161
174
|
|
162
175
|
@event.listens_for(AdminUser, "before_insert")
|
163
176
|
def before_insert(mapper, connection, target):
|
164
|
-
|
165
|
-
|
177
|
+
from hiddifypanel import hutils
|
178
|
+
hutils.model.gen_username(target)
|
179
|
+
hutils.model.gen_password(target)
|
@@ -2,13 +2,13 @@ import datetime
|
|
2
2
|
import uuid
|
3
3
|
from hiddifypanel.models.role import Role
|
4
4
|
from sqlalchemy import Column, String, BigInteger, Enum
|
5
|
-
|
5
|
+
|
6
6
|
from flask_login import UserMixin as FlaskLoginUserMixin
|
7
7
|
from hiddifypanel.models import Lang
|
8
8
|
from hiddifypanel.database import db
|
9
9
|
|
10
10
|
|
11
|
-
class BaseAccount(db.Model,
|
11
|
+
class BaseAccount(db.Model, FlaskLoginUserMixin): # type: ignore
|
12
12
|
__abstract__ = True
|
13
13
|
uuid = Column(String(36), default=lambda: str(uuid.uuid4()), nullable=False, unique=True, index=True)
|
14
14
|
name = Column(String(512), nullable=False, default='')
|
@@ -61,12 +61,12 @@ class BaseAccount(db.Model, SerializerMixin, FlaskLoginUserMixin): # type: igno
|
|
61
61
|
def add_or_update(cls, commit: bool = True, **data):
|
62
62
|
from hiddifypanel import hutils
|
63
63
|
db_account = cls.by_uuid(data['uuid'], create=True)
|
64
|
-
db_account.name = data.get('name'
|
64
|
+
db_account.name = data.get('name', '')
|
65
65
|
db_account.comment = data.get('comment', '')
|
66
66
|
db_account.telegram_id = hutils.convert.to_int(data.get('telegram_id'))
|
67
67
|
db_account.lang = data.get('lang')
|
68
68
|
if commit:
|
69
|
-
db.session.commit()
|
69
|
+
db.session.commit() # type: ignore
|
70
70
|
return db_account
|
71
71
|
|
72
72
|
@classmethod
|
@@ -74,9 +74,9 @@ class BaseAccount(db.Model, SerializerMixin, FlaskLoginUserMixin): # type: igno
|
|
74
74
|
for u in accounts:
|
75
75
|
cls.add_or_update(commit=False, **u)
|
76
76
|
if remove:
|
77
|
-
dd = {u['uuid']: 1 for u in accounts}
|
77
|
+
dd = {str(u['uuid']): 1 for u in accounts}
|
78
78
|
for d in cls.query.all():
|
79
79
|
if d.uuid not in dd:
|
80
|
-
db.session.delete(d)
|
80
|
+
db.session.delete(d) # type: ignore
|
81
81
|
if commit:
|
82
|
-
db.session.commit()
|
82
|
+
db.session.commit() # type: ignore
|
hiddifypanel/models/child.py
CHANGED
@@ -1,30 +1,34 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import uuid
|
3
|
-
|
3
|
+
|
4
|
+
from sqlalchemy import Column, Integer, String, Enum, text
|
4
5
|
from enum import auto
|
5
6
|
from strenum import StrEnum
|
6
7
|
from flask import g, has_app_context
|
7
8
|
|
8
9
|
|
9
|
-
from hiddifypanel.database import db
|
10
|
+
from hiddifypanel.database import db, db_execute
|
10
11
|
|
11
12
|
|
12
13
|
class ChildMode(StrEnum):
|
13
14
|
virtual = auto()
|
14
|
-
remote = auto()
|
15
|
+
remote = auto() # it's child
|
16
|
+
parent = auto()
|
17
|
+
|
18
|
+
# the child model is node
|
15
19
|
|
16
20
|
|
17
|
-
class Child(db.Model
|
18
|
-
id =
|
19
|
-
name =
|
20
|
-
mode =
|
21
|
+
class Child(db.Model): # type: ignore
|
22
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
23
|
+
name = Column(String(200), nullable=False, unique=False)
|
24
|
+
mode = Column(Enum(ChildMode), nullable=False, default=ChildMode.virtual)
|
21
25
|
# ip = db.Column(db.String(200), nullable=False, unique=True)
|
22
|
-
unique_id =
|
23
|
-
domains = db.relationship('Domain', cascade="all,delete", backref='child')
|
24
|
-
proxies = db.relationship('Proxy', cascade="all,delete", backref='child')
|
25
|
-
boolconfigs = db.relationship('BoolConfig', cascade="all,delete", backref='child')
|
26
|
-
strconfigs = db.relationship('StrConfig', cascade="all,delete", backref='child')
|
27
|
-
dailyusages = db.relationship('DailyUsage', cascade="all,delete", backref='child')
|
26
|
+
unique_id = Column(String(200), nullable=False, default=lambda: str(uuid.uuid4()), unique=True)
|
27
|
+
domains = db.relationship('Domain', cascade="all,delete", backref='child') # type: ignore
|
28
|
+
proxies = db.relationship('Proxy', cascade="all,delete", backref='child') # type: ignore
|
29
|
+
boolconfigs = db.relationship('BoolConfig', cascade="all,delete", backref='child') # type: ignore
|
30
|
+
strconfigs = db.relationship('StrConfig', cascade="all,delete", backref='child') # type: ignore
|
31
|
+
dailyusages = db.relationship('DailyUsage', cascade="all,delete", backref='child') # type: ignore
|
28
32
|
|
29
33
|
def to_dict(self):
|
30
34
|
return {
|
@@ -55,11 +59,14 @@ class Child(db.Model, SerializerMixin):
|
|
55
59
|
db.session.commit()
|
56
60
|
|
57
61
|
@classmethod
|
58
|
-
def by_id(cls, id: int) ->
|
62
|
+
def by_id(cls, id: int) -> 'Child':
|
59
63
|
return Child.query.filter(Child.id == id).first()
|
60
64
|
|
61
65
|
@classmethod
|
62
|
-
|
66
|
+
def by_unique_id(cls, unique_id: str) -> 'Child':
|
67
|
+
return Child.query.filter(Child.unique_id == unique_id).first()
|
68
|
+
|
69
|
+
@classmethod
|
63
70
|
def current(cls) -> "Child":
|
64
71
|
if has_app_context() and hasattr(g, "child"):
|
65
72
|
return g.child
|
@@ -68,6 +75,13 @@ class Child(db.Model, SerializerMixin):
|
|
68
75
|
tmp_uuid = str(uuid.uuid4())
|
69
76
|
db.session.add(Child(id=0, unique_id=tmp_uuid, name="Root"))
|
70
77
|
db.session.commit()
|
71
|
-
|
78
|
+
db_execute(f"update child set id=0 where unique_id='{tmp_uuid}'")
|
72
79
|
child = Child.by_id(0)
|
80
|
+
print("child-=======", child)
|
73
81
|
return child
|
82
|
+
|
83
|
+
@classmethod
|
84
|
+
@property
|
85
|
+
def node(cls) -> "Child | None":
|
86
|
+
if has_app_context() and hasattr(g, "node"):
|
87
|
+
return g.node
|