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.
- hiddifypanel/VERSION +1 -1
- hiddifypanel/VERSION.py +2 -2
- hiddifypanel/auth.py +30 -9
- hiddifypanel/base.py +58 -50
- 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 -1848
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +2019 -1874
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +1873 -1742
- hiddifypanel/translations.i18n/en.json +992 -933
- hiddifypanel/translations.i18n/fa.json +994 -935
- hiddifypanel/translations.i18n/pt.json +1031 -972
- hiddifypanel/translations.i18n/ru.json +994 -935
- hiddifypanel/translations.i18n/zh.json +971 -912
- {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/METADATA +47 -47
- {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/RECORD +147 -120
- {hiddifypanel-9.0.0.dev92.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.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-9.0.0.dev92.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/top_level.txt +0 -0
@@ -346,31 +346,25 @@ class AppAPI(MethodView):
|
|
346
346
|
|
347
347
|
if self.platform == Platform.all:
|
348
348
|
platform = [Platform.windows, Platform.linux, Platform.mac]
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
match p:
|
353
|
-
case Platform.windows:
|
354
|
-
ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyClashDesktop_{version}_x64_en-US.msi'
|
355
|
-
dto.install.append(self.__get_app_install_dto(AppInstallType.setup, ins_url))
|
356
|
-
case Platform.linux:
|
357
|
-
ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/hiddify-clash-desktop_{version}_amd64.AppImage'
|
358
|
-
dto.install.append(self.__get_app_install_dto(AppInstallType.appimage, ins_url))
|
359
|
-
case Platform.mac:
|
360
|
-
ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyClashDesktop_{version}x64.dmg'
|
361
|
-
dto.install.append(self.__get_app_install_dto(AppInstallType.dmg, ins_url))
|
362
|
-
else:
|
363
|
-
match platform:
|
349
|
+
|
350
|
+
def get_link(p):
|
351
|
+
match p:
|
364
352
|
case Platform.windows:
|
365
|
-
ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyClashDesktop_{version}_x64_en-US.msi'
|
353
|
+
ins_url = latest_url.split('releases/')[0] + f'releases/download/v{version}/HiddifyClashDesktop_{version}_x64_en-US.msi'
|
366
354
|
dto.install.append(self.__get_app_install_dto(AppInstallType.setup, ins_url))
|
367
355
|
case Platform.linux:
|
368
|
-
ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/hiddify-clash-desktop_{version}_amd64.AppImage'
|
356
|
+
ins_url = latest_url.split('releases/')[0] + f'releases/download/v{version}/hiddify-clash-desktop_{version}_amd64.AppImage'
|
369
357
|
dto.install.append(self.__get_app_install_dto(AppInstallType.appimage, ins_url))
|
370
358
|
case Platform.mac:
|
371
|
-
ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyClashDesktop_{version}
|
359
|
+
ins_url = latest_url.split('releases/')[0] + f'releases/download/v{version}/HiddifyClashDesktop_{version}_x64.dmg'
|
372
360
|
dto.install.append(self.__get_app_install_dto(AppInstallType.dmg, ins_url))
|
373
361
|
|
362
|
+
if isinstance(platform, list):
|
363
|
+
for p in platform:
|
364
|
+
get_link(p)
|
365
|
+
else:
|
366
|
+
get_link(platform)
|
367
|
+
|
374
368
|
return dto
|
375
369
|
|
376
370
|
def __get_hiddify_next_app_dto(self):
|
@@ -402,17 +396,17 @@ class AppAPI(MethodView):
|
|
402
396
|
ins_url = ''
|
403
397
|
match install_type:
|
404
398
|
case AppInstallType.apk:
|
405
|
-
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/
|
399
|
+
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/Hiddify-Android-universal.apk'
|
406
400
|
case AppInstallType.google_play:
|
407
401
|
ins_url = 'https://play.google.com/store/apps/details?id=app.hiddify.com'
|
408
402
|
case AppInstallType.setup:
|
409
|
-
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/
|
403
|
+
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/Hiddify-Windows-Setup-x64.exe'
|
410
404
|
case AppInstallType.portable:
|
411
|
-
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/
|
405
|
+
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/Hiddify-Windows-Portable-x64.zip'
|
412
406
|
case AppInstallType.appimage:
|
413
|
-
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/
|
407
|
+
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/Hiddify-Linux-x64.AppImage'
|
414
408
|
case AppInstallType.dmg:
|
415
|
-
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/
|
409
|
+
ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/Hiddify-MacOS.dmg'
|
416
410
|
|
417
411
|
install_dto = self.__get_app_install_dto(install_type, ins_url)
|
418
412
|
install_dtos.append(install_dto)
|
@@ -3,13 +3,10 @@ from flask import g, request
|
|
3
3
|
from flask import current_app as app
|
4
4
|
from flask.views import MethodView
|
5
5
|
from hiddifypanel.auth import login_required
|
6
|
-
from hiddifypanel.models
|
7
|
-
from hiddifypanel.models.config_enum import ConfigEnum
|
8
|
-
from hiddifypanel.models.role import Role
|
6
|
+
from hiddifypanel.models import Proxy, Role, ConfigEnum, hconfig
|
9
7
|
from apiflask import Schema
|
10
8
|
from apiflask.fields import String
|
11
9
|
from hiddifypanel.panel.user.user import get_common_data
|
12
|
-
from hiddifypanel.panel.user import link_maker
|
13
10
|
from hiddifypanel import hutils
|
14
11
|
|
15
12
|
|
@@ -26,7 +23,7 @@ class ConfigSchema(Schema):
|
|
26
23
|
class AllConfigsAPI(MethodView):
|
27
24
|
decorators = [login_required({Role.user})]
|
28
25
|
|
29
|
-
@app.output(ConfigSchema(many=True))
|
26
|
+
@app.output(ConfigSchema(many=True)) # type: ignore
|
30
27
|
def get(self):
|
31
28
|
def create_item(name, domain, type, protocol, transport, security, link):
|
32
29
|
dto = ConfigSchema()
|
@@ -62,6 +59,24 @@ class AllConfigsAPI(MethodView):
|
|
62
59
|
)
|
63
60
|
)
|
64
61
|
|
62
|
+
# Add Subscription link
|
63
|
+
items.append(
|
64
|
+
create_item(
|
65
|
+
"Subscription link", "ALL", "ALL", "", "", "",
|
66
|
+
# f"{base_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
|
67
|
+
f"{base_url}sub/?asn={c['asn']}#{config_name}"
|
68
|
+
)
|
69
|
+
)
|
70
|
+
|
71
|
+
# Add Subscription link base64
|
72
|
+
items.append(
|
73
|
+
create_item(
|
74
|
+
"Subscription link b64", "ALL", "ALL", "", "", "",
|
75
|
+
# f"{base_url}all.txt?name=new_link_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}&base64=True"
|
76
|
+
f"{base_url}sub64/?asn={c['asn']}#{config_name}"
|
77
|
+
)
|
78
|
+
)
|
79
|
+
|
65
80
|
# Add Clash Meta
|
66
81
|
items.append(
|
67
82
|
create_item(
|
@@ -84,31 +99,13 @@ class AllConfigsAPI(MethodView):
|
|
84
99
|
if hconfig(ConfigEnum.ssh_server_enable):
|
85
100
|
items.append(
|
86
101
|
create_item(
|
87
|
-
"Singbox: SSH", "SSH", "
|
102
|
+
"Singbox: SSH", "SSH", "SSH", "", "", "",
|
88
103
|
# f"{base_url}singbox.json?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
|
89
104
|
f"{base_url}singbox-ssh/?asn={c['asn']}#{config_name}"
|
90
105
|
)
|
91
106
|
)
|
92
107
|
|
93
|
-
|
94
|
-
items.append(
|
95
|
-
create_item(
|
96
|
-
"Subscription link", "ALL", "ALL", "", "", "",
|
97
|
-
# f"{base_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
|
98
|
-
f"{base_url}sub/?asn={c['asn']}#{config_name}"
|
99
|
-
)
|
100
|
-
)
|
101
|
-
|
102
|
-
# Add Subscription link base64
|
103
|
-
items.append(
|
104
|
-
create_item(
|
105
|
-
"Subscription link b64", "ALL", "ALL", "", "", "",
|
106
|
-
# f"{base_url}all.txt?name=new_link_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}&base64=True"
|
107
|
-
f"{base_url}sub64/?asn={c['asn']}#{config_name}"
|
108
|
-
)
|
109
|
-
)
|
110
|
-
|
111
|
-
for pinfo in link_maker.get_all_validated_proxies(c['domains']):
|
108
|
+
for pinfo in hutils.proxy.get_valid_proxies(c['domains']):
|
112
109
|
items.append(
|
113
110
|
create_item(
|
114
111
|
pinfo["name"].replace("_", " "),
|
@@ -117,7 +114,7 @@ class AllConfigsAPI(MethodView):
|
|
117
114
|
pinfo['proto'],
|
118
115
|
pinfo['transport'],
|
119
116
|
pinfo['l3'],
|
120
|
-
f"{
|
117
|
+
f"{hutils.proxy.xray.to_link(pinfo)}"
|
121
118
|
)
|
122
119
|
)
|
123
120
|
|
@@ -138,8 +138,7 @@ def create_package(call): # <- passes a CallbackQuery type object to your funct
|
|
138
138
|
domain = int(splt[4])
|
139
139
|
new_text = _("Please Wait...")
|
140
140
|
bot.edit_message_text(new_text, call.message.chat.id, call.message.message_id, reply_markup=None)
|
141
|
-
|
142
|
-
domain = DT.query.filter(DT.id == domain).first()
|
141
|
+
domain = Domain.query.filter(Domain.id == domain).first()
|
143
142
|
from . import Usage
|
144
143
|
admin_id = admin.id
|
145
144
|
admin_name = admin.name
|
hiddifypanel/panel/common.py
CHANGED
@@ -9,6 +9,7 @@ from hiddifypanel import hutils
|
|
9
9
|
import hiddifypanel.auth as auth
|
10
10
|
from hiddifypanel.auth import current_account
|
11
11
|
from apiflask import APIFlask, HTTPError, abort
|
12
|
+
from hiddifypanel import hutils
|
12
13
|
|
13
14
|
|
14
15
|
def init_app(app: APIFlask):
|
@@ -17,10 +18,13 @@ def init_app(app: APIFlask):
|
|
17
18
|
app.jinja_env.globals['UserMode'] = UserMode
|
18
19
|
app.jinja_env.globals['hconfig'] = hconfig
|
19
20
|
app.jinja_env.globals['g'] = g
|
21
|
+
app.jinja_env.globals['hutils'] = hutils
|
22
|
+
app.jinja_env.globals['hiddify'] = hiddify
|
20
23
|
app.jinja_env.globals['version'] = hiddifypanel.__version__
|
21
24
|
app.jinja_env.globals['static_url_for'] = hutils.flask.static_url_for
|
22
25
|
app.jinja_env.globals['hurl_for'] = hutils.flask.hurl_for
|
23
26
|
app.jinja_env.globals['_gettext'] = lambda x: print("==========", x)
|
27
|
+
app.jinja_env.globals['proxy_stats_url'] = hutils.flask.get_proxy_stats_url
|
24
28
|
|
25
29
|
@app.after_request
|
26
30
|
def apply_no_robot(response):
|
@@ -32,6 +36,12 @@ def init_app(app: APIFlask):
|
|
32
36
|
|
33
37
|
@app.errorhandler(Exception)
|
34
38
|
def internal_server_error(e):
|
39
|
+
if isinstance(e, Exception):
|
40
|
+
if hutils.flask.is_api_call(request.path):
|
41
|
+
return {
|
42
|
+
'msg': str(e),
|
43
|
+
}, 500
|
44
|
+
|
35
45
|
if hasattr(e, 'code') and e.code == 404:
|
36
46
|
return jsonify({
|
37
47
|
'message': 'Not Found',
|
@@ -66,6 +76,7 @@ def init_app(app: APIFlask):
|
|
66
76
|
# print(request.headers)
|
67
77
|
if not request.accept_mimetypes.accept_html:
|
68
78
|
return app.error_callback(e)
|
79
|
+
# if it's interval server error
|
69
80
|
if e.status_code == 500:
|
70
81
|
trace = traceback.format_exc()
|
71
82
|
|
@@ -79,9 +90,17 @@ def init_app(app: APIFlask):
|
|
79
90
|
has_update = "dev" not in hiddifypanel.__version__ and f'{last_version}' != hiddifypanel.__version__
|
80
91
|
|
81
92
|
return render_template('500.html', error=e, trace=trace, has_update=has_update, last_version=last_version, issue_link=issue_link), 500
|
93
|
+
|
94
|
+
# if it's access denied error
|
82
95
|
# if e.status_code in [400,401,403]:
|
83
96
|
# return render_template('access-denied.html',error=e), e.status_code
|
84
97
|
|
98
|
+
# if it's api error
|
99
|
+
if hutils.flask.is_api_call(request.path):
|
100
|
+
return {
|
101
|
+
'msg': e.message,
|
102
|
+
}, e.status_code
|
103
|
+
|
85
104
|
return render_template('error.html', error=e), e.status_code
|
86
105
|
|
87
106
|
@app.url_defaults
|
@@ -91,11 +110,11 @@ def init_app(app: APIFlask):
|
|
91
110
|
if 'proxy_path' not in values:
|
92
111
|
if force_path := g.get('force_proxy_path'):
|
93
112
|
values['proxy_path'] = force_path
|
94
|
-
elif hutils.flask.is_admin_role(current_account.role):
|
113
|
+
elif hutils.flask.is_admin_role(current_account.role): # type: ignore
|
95
114
|
values['proxy_path'] = hconfig(ConfigEnum.proxy_path_admin)
|
96
115
|
elif hutils.flask.is_user_panel_call():
|
97
116
|
values['proxy_path'] = hconfig(ConfigEnum.proxy_path_client)
|
98
|
-
elif current_account and hutils.flask.is_admin_role(current_account.role):
|
117
|
+
elif current_account and hutils.flask.is_admin_role(current_account.role): # type: ignore
|
99
118
|
values['proxy_path'] = hconfig(ConfigEnum.proxy_path_admin)
|
100
119
|
else:
|
101
120
|
values['proxy_path'] = g.proxy_path or "A"
|
@@ -131,12 +150,6 @@ def init_app(app: APIFlask):
|
|
131
150
|
g.child = Child.by_id(g.__child_id) or abort(404, "Child not found")
|
132
151
|
g.account = current_account
|
133
152
|
|
134
|
-
@app.before_first_request
|
135
|
-
def first_request():
|
136
|
-
import hiddifypanel.panel.commercial.telegrambot as telegrambot
|
137
|
-
if (not telegrambot.bot) or (not telegrambot.bot.username): # type: ignore
|
138
|
-
telegrambot.register_bot(set_hook=True)
|
139
|
-
|
140
153
|
@app.before_request
|
141
154
|
def base_middleware():
|
142
155
|
if request.endpoint == 'static' or request.endpoint == "videos":
|
@@ -172,3 +185,7 @@ def init_app(app: APIFlask):
|
|
172
185
|
return auth_before
|
173
186
|
|
174
187
|
app.jinja_env.globals['generate_github_issue_link_for_admin_sidebar'] = hutils.github_issue.generate_github_issue_link_for_admin_sidebar
|
188
|
+
with app.app_context():
|
189
|
+
import hiddifypanel.panel.commercial.telegrambot as telegrambot
|
190
|
+
if (not telegrambot.bot) or (not telegrambot.bot.username): # type: ignore
|
191
|
+
telegrambot.register_bot(set_hook=True)
|
@@ -42,8 +42,8 @@ class LoginView(FlaskView):
|
|
42
42
|
return redirect(redirect_arg)
|
43
43
|
if hutils.flask.is_admin_proxy_path() and g.account.role in {Role.super_admin, Role.admin, Role.agent}:
|
44
44
|
return redirect(hurl_for('admin.Dashboard:index'))
|
45
|
-
if g.user_agent['is_browser'] and hutils.flask.is_client_proxy_path():
|
46
|
-
|
45
|
+
# if g.user_agent['is_browser'] and hutils.flask.is_client_proxy_path():
|
46
|
+
# return redirect(hurl_for('client.UserView:index'))
|
47
47
|
|
48
48
|
from hiddifypanel.panel.user import UserView
|
49
49
|
return UserView().auto_sub()
|
hiddifypanel/panel/hiddify.py
CHANGED
@@ -1,23 +1,16 @@
|
|
1
|
-
import glob
|
2
1
|
import re
|
3
|
-
import json
|
4
2
|
import subprocess
|
5
3
|
|
6
4
|
from datetime import datetime
|
7
5
|
from typing import Tuple
|
8
|
-
from cryptography.hazmat.primitives import serialization
|
9
|
-
from cryptography.hazmat.primitives.asymmetric import x25519
|
10
6
|
from flask import current_app, g
|
11
|
-
from wtforms.validators import ValidationError
|
12
7
|
from flask_babel import lazy_gettext as _
|
13
|
-
from flask_babel import gettext as __
|
14
8
|
from datetime import timedelta
|
15
9
|
|
16
10
|
from hiddifypanel.cache import cache
|
17
11
|
from hiddifypanel.models import *
|
18
12
|
from hiddifypanel.database import db
|
19
13
|
from hiddifypanel.hutils.utils import *
|
20
|
-
from hiddifypanel.Events import domain_changed
|
21
14
|
from hiddifypanel import hutils
|
22
15
|
from hiddifypanel.panel.run_commander import commander, Command
|
23
16
|
import subprocess
|
@@ -69,70 +62,10 @@ def exec_command(cmd, cwd=None):
|
|
69
62
|
print(e)
|
70
63
|
|
71
64
|
|
72
|
-
@cache.cache(ttl=300)
|
73
|
-
def get_available_proxies(child_id):
|
74
|
-
proxies = Proxy.query.filter(Proxy.child_id == child_id).all()
|
75
|
-
proxies = [c for c in proxies if 'restls' not in c.transport]
|
76
|
-
# if not hconfig(ConfigEnum.tuic_enable, child_id):
|
77
|
-
# proxies = [c for c in proxies if c.proto != ProxyProto.tuic]
|
78
|
-
# if not hconfig(ConfigEnum.hysteria_enable, child_id):
|
79
|
-
# proxies = [c for c in proxies if c.proto != ProxyProto.hysteria2]
|
80
|
-
if not hconfig(ConfigEnum.shadowsocks2022_enable, child_id):
|
81
|
-
proxies = [c for c in proxies if 'shadowsocks' != c.transport]
|
82
|
-
|
83
|
-
if not hconfig(ConfigEnum.ssfaketls_enable, child_id):
|
84
|
-
proxies = [c for c in proxies if 'faketls' != c.transport]
|
85
|
-
if not hconfig(ConfigEnum.v2ray_enable, child_id):
|
86
|
-
proxies = [c for c in proxies if 'v2ray' != c.proto]
|
87
|
-
if not hconfig(ConfigEnum.shadowtls_enable, child_id):
|
88
|
-
proxies = [c for c in proxies if c.transport != 'shadowtls']
|
89
|
-
if not hconfig(ConfigEnum.ssr_enable, child_id):
|
90
|
-
proxies = [c for c in proxies if 'ssr' != c.proto]
|
91
|
-
if not hconfig(ConfigEnum.vmess_enable, child_id):
|
92
|
-
proxies = [c for c in proxies if 'vmess' not in c.proto]
|
93
|
-
if not hconfig(ConfigEnum.httpupgrade_enable, child_id):
|
94
|
-
proxies = [c for c in proxies if ProxyTransport.httpupgrade not in c.transport]
|
95
|
-
if not hconfig(ConfigEnum.ws_enable, child_id):
|
96
|
-
proxies = [c for c in proxies if ProxyTransport.WS not in c.transport]
|
97
|
-
|
98
|
-
if not hconfig(ConfigEnum.grpc_enable, child_id):
|
99
|
-
proxies = [c for c in proxies if ProxyTransport.grpc not in c.transport]
|
100
|
-
if not hconfig(ConfigEnum.kcp_enable, child_id):
|
101
|
-
proxies = [c for c in proxies if 'kcp' not in c.l3]
|
102
|
-
|
103
|
-
if not hconfig(ConfigEnum.http_proxy_enable, child_id):
|
104
|
-
proxies = [c for c in proxies if 'http' != c.l3]
|
105
|
-
|
106
|
-
if not Domain.query.filter(Domain.mode.in_([DomainType.cdn, DomainType.auto_cdn_ip])).first():
|
107
|
-
proxies = [c for c in proxies if c.cdn != "CDN"]
|
108
|
-
|
109
|
-
if not Domain.query.filter(Domain.mode.in_([DomainType.relay])).first():
|
110
|
-
proxies = [c for c in proxies if c.cdn != ProxyCDN.relay]
|
111
|
-
|
112
|
-
if not Domain.query.filter(Domain.mode.in_([DomainType.cdn, DomainType.auto_cdn_ip]), Domain.servernames != "", Domain.servernames != Domain.domain).first():
|
113
|
-
proxies = [c for c in proxies if 'Fake' not in c.cdn]
|
114
|
-
proxies = [c for c in proxies if not ('vless' == c.proto and ProxyTransport.tcp == c.transport and c.cdn == ProxyCDN.direct)]
|
115
|
-
return proxies
|
116
|
-
|
117
|
-
|
118
65
|
def quick_apply_users():
|
119
|
-
if hconfig(ConfigEnum.is_parent):
|
120
|
-
return
|
121
|
-
# from hiddifypanel.panel import usage
|
122
|
-
# usage.update_local_usage()
|
123
|
-
# return
|
124
|
-
# for user in User.query.all():
|
125
|
-
# if user.is_active:
|
126
|
-
# xray_api.add_client(user.uuid)
|
127
|
-
# else:
|
128
|
-
# xray_api.remove_client(user.uuid)
|
129
|
-
|
130
|
-
# exec_command("sudo /opt/hiddify-manager/install.sh apply_users --no-gui")
|
131
|
-
|
132
66
|
# run install.sh apply_users
|
133
67
|
commander(Command.apply_users)
|
134
68
|
|
135
|
-
# time.sleep(1)
|
136
69
|
return {"status": 'success'}
|
137
70
|
|
138
71
|
|
@@ -164,16 +97,6 @@ def get_html_user_link(model: BaseAccount, domain: Domain):
|
|
164
97
|
return res
|
165
98
|
|
166
99
|
|
167
|
-
def validate_domain_exist(form, field):
|
168
|
-
domain = field.data
|
169
|
-
if not domain:
|
170
|
-
return
|
171
|
-
dip = hutils.network.get_domain_ip(domain)
|
172
|
-
if dip is None:
|
173
|
-
raise ValidationError(
|
174
|
-
_("Domain can not be resolved! there is a problem in your domain"))
|
175
|
-
|
176
|
-
|
177
100
|
def reinstall_action(complete_install=False, domain_changed=False, do_update=False):
|
178
101
|
from hiddifypanel.panel.admin.Actions import Actions
|
179
102
|
action = Actions()
|
@@ -202,11 +125,12 @@ def check_need_reset(old_configs, do=False):
|
|
202
125
|
|
203
126
|
|
204
127
|
def get_child(unique_id):
|
205
|
-
child_id = Child.current.id
|
128
|
+
child_id = Child.current().id
|
206
129
|
if unique_id is None or unique_id in ["self", "default", str(hconfig(ConfigEnum.unique_id))]:
|
207
130
|
child_id = 0
|
208
131
|
else:
|
209
132
|
child = Child.query.filter(Child.unique_id == str(unique_id)).first()
|
133
|
+
# TODO: this doesn't work because name and mode fields are nullable
|
210
134
|
if not child:
|
211
135
|
child = Child(unique_id=str(unique_id))
|
212
136
|
db.session.add(child)
|
@@ -221,7 +145,7 @@ def dump_db_to_dict():
|
|
221
145
|
"users": [u.to_dict() for u in User.query.all()],
|
222
146
|
"domains": [u.to_dict() for u in Domain.query.all()],
|
223
147
|
"proxies": [u.to_dict() for u in Proxy.query.all()],
|
224
|
-
"parent_domains": [] if not hconfig(ConfigEnum.license) else [u.to_dict() for u in ParentDomain.query.all()],
|
148
|
+
# "parent_domains": [] if not hconfig(ConfigEnum.license) else [u.to_dict() for u in ParentDomain.query.all()],
|
225
149
|
'admin_users': [d.to_dict() for d in AdminUser.query.all()],
|
226
150
|
"hconfigs": [*[u.to_dict() for u in BoolConfig.query.all()],
|
227
151
|
*[u.to_dict() for u in StrConfig.query.all()]]
|
@@ -252,7 +176,7 @@ def set_db_from_json(json_data, override_child_unique_id=True, set_users=True, s
|
|
252
176
|
# override root child unique id
|
253
177
|
if override_child_unique_id:
|
254
178
|
backup_child_unique_id = get_backup_child_unique_id(json_data)
|
255
|
-
replace_backup_child_unique_id(json_data, backup_child_unique_id, Child.current.unique_id)
|
179
|
+
replace_backup_child_unique_id(json_data, backup_child_unique_id, Child.current().unique_id)
|
256
180
|
|
257
181
|
# restore childs
|
258
182
|
if set_child and 'childs' in json_data:
|
@@ -296,17 +220,16 @@ def set_db_from_json(json_data, override_child_unique_id=True, set_users=True, s
|
|
296
220
|
u['added_by_uuid'] = AdminUser.current_admin_or_owner().uuid
|
297
221
|
|
298
222
|
if set_admins and 'admin_users' in json_data:
|
299
|
-
AdminUser.bulk_register(json_data['admin_users'], commit=
|
223
|
+
AdminUser.bulk_register(json_data['admin_users'], commit=True)
|
300
224
|
if set_users and 'users' in json_data:
|
301
225
|
User.bulk_register(json_data['users'], commit=False, remove=remove_users)
|
302
226
|
if set_domains and 'domains' in json_data:
|
303
|
-
bulk_register_domains(json_data['domains'], commit=False, remove=remove_domains
|
304
|
-
|
305
|
-
# ParentDomain.bulk_register(json_data['parent_domains'], commit=False, remove=remove_domains)
|
227
|
+
bulk_register_domains(json_data['domains'], commit=False, remove=remove_domains)
|
228
|
+
|
306
229
|
if set_settings and 'hconfigs' in json_data:
|
307
|
-
bulk_register_configs(json_data["hconfigs"], commit=True,
|
230
|
+
bulk_register_configs(json_data["hconfigs"], commit=True, override_unique_id=override_unique_id)
|
308
231
|
if 'proxies' in json_data:
|
309
|
-
Proxy.bulk_register(json_data['proxies'], commit=False
|
232
|
+
Proxy.bulk_register(json_data['proxies'], commit=False)
|
310
233
|
|
311
234
|
ids_without_parent = get_ids_without_parent({u.id: u.to_dict() for u in AdminUser.query.all()})
|
312
235
|
owner = AdminUser.get_super_admin()
|
@@ -333,93 +256,13 @@ def get_domain_btn_link(domain):
|
|
333
256
|
return res
|
334
257
|
|
335
258
|
|
336
|
-
def debug_flash_if_not_in_the_same_asn(domain):
|
337
|
-
from hiddifypanel.hutils.network.auto_ip_selector import IPASN
|
338
|
-
ipv4 = hutils.network.get_ip_str(4)
|
339
|
-
dip = hutils.network.get_domain_ip(domain)
|
340
|
-
try:
|
341
|
-
if IPASN:
|
342
|
-
asn_ipv4 = IPASN.get(ipv4)
|
343
|
-
asn_dip = IPASN.get(dip)
|
344
|
-
# country_ipv4= ipcountry.get(ipv4)
|
345
|
-
# country_dip= ipcountry.get(dip)
|
346
|
-
if asn_ipv4.get('autonomous_system_organization') != asn_dip.get('autonomous_system_organization'):
|
347
|
-
hutils.flask.flash(_("selected domain for REALITY is not in the same ASN. To better use of the protocol, it is better to find a domain in the same ASN.") +
|
348
|
-
f"<br> Server ASN={asn_ipv4.get('autonomous_system_organization','unknown')}<br>{domain}_ASN={asn_dip.get('autonomous_system_organization','unknown')}", "warning")
|
349
|
-
except BaseException:
|
350
|
-
pass
|
351
|
-
|
352
|
-
|
353
|
-
def generate_x25519_keys():
|
354
|
-
priv = x25519.X25519PrivateKey.generate()
|
355
|
-
pub = priv.public_key()
|
356
|
-
priv_bytes = priv.private_bytes(
|
357
|
-
encoding=serialization.Encoding.Raw,
|
358
|
-
format=serialization.PrivateFormat.Raw,
|
359
|
-
encryption_algorithm=serialization.NoEncryption()
|
360
|
-
)
|
361
|
-
pub_bytes = pub.public_bytes(
|
362
|
-
encoding=serialization.Encoding.Raw,
|
363
|
-
format=serialization.PublicFormat.Raw
|
364
|
-
)
|
365
|
-
import base64
|
366
|
-
pub_str = base64.urlsafe_b64encode(pub_bytes).decode()[:-1]
|
367
|
-
priv_str = base64.urlsafe_b64encode(priv_bytes).decode()[:-1]
|
368
|
-
|
369
|
-
return {'private_key': priv_str, 'public_key': pub_str}
|
370
|
-
|
371
|
-
|
372
|
-
def get_hostkeys(dojson=False):
|
373
|
-
key_files = glob.glob(current_app.config['HIDDIFY_CONFIG_PATH'] + "/other/ssh/host_key/*_key.pub")
|
374
|
-
host_keys = []
|
375
|
-
for file_name in key_files:
|
376
|
-
with open(file_name, "r") as f:
|
377
|
-
host_key = f.read().strip()
|
378
|
-
host_key = host_key.split()
|
379
|
-
if len(host_key) > 2:
|
380
|
-
host_key = host_key[:2] # strip the hostname part
|
381
|
-
host_key = " ".join(host_key)
|
382
|
-
host_keys.append(host_key)
|
383
|
-
if dojson:
|
384
|
-
return json.dumps(host_keys)
|
385
|
-
return host_keys
|
386
|
-
|
387
|
-
|
388
259
|
def get_ssh_client_version(user):
|
389
260
|
return 'SSH-2.0-OpenSSH_7.4p1'
|
390
261
|
|
391
262
|
|
392
|
-
def get_ed25519_private_public_pair():
|
393
|
-
from cryptography.hazmat.primitives.asymmetric import ed25519
|
394
|
-
from cryptography.hazmat.primitives import serialization
|
395
|
-
privkey = ed25519.Ed25519PrivateKey.generate()
|
396
|
-
pubkey = privkey.public_key()
|
397
|
-
priv_bytes = privkey.private_bytes(
|
398
|
-
encoding=serialization.Encoding.PEM,
|
399
|
-
format=serialization.PrivateFormat.OpenSSH,
|
400
|
-
encryption_algorithm=serialization.NoEncryption(),
|
401
|
-
)
|
402
|
-
pub_bytes = pubkey.public_bytes(
|
403
|
-
encoding=serialization.Encoding.OpenSSH,
|
404
|
-
format=serialization.PublicFormat.OpenSSH,
|
405
|
-
)
|
406
|
-
return priv_bytes.decode(), pub_bytes.decode()
|
407
|
-
|
408
|
-
|
409
|
-
def get_wg_private_public_psk_pair():
|
410
|
-
try:
|
411
|
-
private_key = subprocess.run(["wg", "genkey"], capture_output=True, text=True, check=True).stdout.strip()
|
412
|
-
public_key = subprocess.run(["wg", "pubkey"], input=private_key, capture_output=True, text=True, check=True).stdout.strip()
|
413
|
-
psk = subprocess.run(["wg", "genpsk"], capture_output=True, text=True, check=True).stdout.strip()
|
414
|
-
return private_key, public_key, psk
|
415
|
-
except subprocess.CalledProcessError as e:
|
416
|
-
print(f"Error: {e}")
|
417
|
-
return None, None, None
|
418
|
-
|
419
|
-
|
420
263
|
def get_account_panel_link(account: BaseAccount, host: str, is_https: bool = True, prefere_path_only: bool = False, child_id=None):
|
421
264
|
if child_id is None:
|
422
|
-
child_id = Child.current.id
|
265
|
+
child_id = Child.current().id
|
423
266
|
is_admin = isinstance(account, AdminUser)
|
424
267
|
basic_auth = False # is_admin #because safri does not support it.
|
425
268
|
|
@@ -441,9 +284,18 @@ def get_account_panel_link(account: BaseAccount, host: str, is_https: bool = Tru
|
|
441
284
|
return link
|
442
285
|
|
443
286
|
|
444
|
-
def is_telegram_proxy_enable() -> bool:
|
445
|
-
|
446
|
-
|
287
|
+
def is_telegram_proxy_enable(domains=None) -> bool:
|
288
|
+
if not hconfig(ConfigEnum.telegram_enable):
|
289
|
+
return False
|
290
|
+
|
291
|
+
valid_domain_types = [DomainType.direct, DomainType.relay, DomainType.old_xtls_direct]
|
292
|
+
res = False
|
293
|
+
if domains:
|
294
|
+
res = any(d.mode in valid_domain_types for d in domains)
|
295
|
+
else:
|
296
|
+
res = True if Domain.query.filter(Domain.mode.in_(valid_domain_types)).first() else False
|
297
|
+
|
298
|
+
return res
|
447
299
|
|
448
300
|
|
449
301
|
def clone_model(model):
|
@@ -455,11 +307,8 @@ def clone_model(model):
|
|
455
307
|
for k in table.columns.keys():
|
456
308
|
if k == "id":
|
457
309
|
continue
|
458
|
-
# if k in table.primary_key:
|
459
|
-
# continue
|
460
310
|
setattr(new_model, f'{k}', getattr(model, k))
|
461
311
|
|
462
|
-
# data.pop('id')
|
463
312
|
return new_model
|
464
313
|
|
465
314
|
|
@@ -484,18 +333,6 @@ def get_backup_child_unique_id(backupdata: dict) -> str:
|
|
484
333
|
return "self"
|
485
334
|
return backupdata['childs'][0]['unique_id']
|
486
335
|
|
487
|
-
# for k, v in backupdata.items():
|
488
|
-
# if k == 'admin_users' or k == 'users':
|
489
|
-
# continue
|
490
|
-
# if k == 'childs':
|
491
|
-
# if len(v) < 1:
|
492
|
-
# continue
|
493
|
-
# return v[0]['unique_id']
|
494
|
-
# else:
|
495
|
-
# for item in v:
|
496
|
-
# return item['child_unique_id']
|
497
|
-
# return 'self'
|
498
|
-
|
499
336
|
|
500
337
|
def is_hiddify_next_version(major_v: int = 0, minor_v: int = 0, patch_v: int = 0) -> bool:
|
501
338
|
'''If the user agent version be equals or higher than parameters returns True'''
|