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
@@ -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
- if isinstance(platform, list):
351
- for p in platform:
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}x64.dmg'
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/hiddify-android-universal.apk'
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/hiddify-windows-x64-setup.zip'
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/hiddify-windows-x64-portable.zip'
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/hiddify-linux-x64.zip'
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/hiddify-macos-universal.zip'
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.config import hconfig
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", "SHH", "", "", "",
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
- # Add Subscription link
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"{link_maker.to_link(pinfo)}"
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
- DT = (ParentDomain if hconfig(ConfigEnum.is_parent) else Domain)
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
@@ -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
- return redirect(hurl_for('client.UserView:index'))
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()
@@ -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=False)
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, override_child_unique_id=override_child_unique_id)
304
- # if set_domains and 'parent_domains' in json_data:
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, override_child_unique_id=override_child_unique_id, override_unique_id=override_unique_id)
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, override_child_unique_id=override_child_unique_id)
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
- ok_domains = True if Domain.query.filter(Domain.mode.in_([DomainType.direct, DomainType.relay, DomainType.old_xtls_direct])).first() else False
446
- return hconfig(ConfigEnum.telegram_enable) and ok_domains
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'''