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.
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 +60 -52
  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 -1840
  133. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  134. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +2036 -1881
  135. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  136. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +1857 -1720
  137. hiddifypanel/translations.i18n/en.json +992 -933
  138. hiddifypanel/translations.i18n/fa.json +994 -935
  139. hiddifypanel/translations.i18n/pt.json +994 -935
  140. hiddifypanel/translations.i18n/ru.json +994 -935
  141. hiddifypanel/translations.i18n/zh.json +971 -912
  142. {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/METADATA +47 -47
  143. {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/RECORD +147 -120
  144. {hiddifypanel-9.0.0.dev90.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.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/LICENSE.md +0 -0
  151. {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/entry_points.txt +0 -0
  152. {hiddifypanel-9.0.0.dev90.dist-info → hiddifypanel-10.5.0.dev0.dist-info}/top_level.txt +0 -0
@@ -39,22 +39,18 @@ class Actions(FlaskView):
39
39
 
40
40
  @login_required(roles={Role.super_admin})
41
41
  def reset2(self):
42
- _ = self.status()
42
+ res = render_template("result.html",
43
+ out_type="info",
44
+ out_msg="",
45
+ log_file_url=get_log_api_url(),
46
+ log_file='restart.log',
47
+ show_success=True,
48
+ domains=get_domains())
43
49
 
44
50
  # run restart.sh
45
51
  commander(Command.restart_services)
46
52
 
47
- # flask don't response at all while using time.sleep
48
- # import time
49
- # time.sleep(1)
50
-
51
- return render_template("result.html",
52
- out_type="info",
53
- out_msg="",
54
- log_file_url=get_log_api_url(),
55
- log_file='restart.log',
56
- show_success=True,
57
- domains=get_domains())
53
+ return res
58
54
 
59
55
  @login_required(roles={Role.super_admin})
60
56
  @route('reinstall', methods=['POST'])
@@ -65,11 +61,9 @@ class Actions(FlaskView):
65
61
  def reinstall2(self, complete_install=True, domain_changed=False):
66
62
  if int(hconfig(ConfigEnum.db_version)) < 9:
67
63
  return ("Please update your panel before this action.")
68
- # if hconfig(ConfigEnum.parent_panel):
69
- # try:
70
- # hiddify_api.sync_child_to_parent()
71
- # except e as Exception:
72
- # hutils.flask.flash(_('can not sync child with parent panel')+" "+e)
64
+ if hutils.node.is_child():
65
+ if not hutils.node.child.sync_with_parent():
66
+ hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
73
67
 
74
68
  domain_changed = request.args.get("domain_changed", str(domain_changed)).lower() == "true"
75
69
  complete_install = request.args.get("complete_install", str(complete_install)).lower() == "true"
@@ -114,7 +108,7 @@ class Actions(FlaskView):
114
108
 
115
109
  @login_required(roles={Role.super_admin})
116
110
  def change_reality_keys(self):
117
- key = hiddify.generate_x25519_keys()
111
+ key = hutils.crypto.generate_x25519_keys()
118
112
  set_hconfig(ConfigEnum.reality_private_key, key['private_key'])
119
113
  set_hconfig(ConfigEnum.reality_public_key, key['public_key'])
120
114
  hutils.flask.flash_config_success(restart_mode=ApplyMode.restart, domain_changed=False)
@@ -170,7 +164,7 @@ class Actions(FlaskView):
170
164
 
171
165
  tcp_ping = hutils.network.is_domain_reality_friendly(d)
172
166
  if tcp_ping:
173
- dip = hutils.network.get_domain_ip(d)
167
+ dip = str(hutils.network.get_domain_ip(d))
174
168
  dip_country = (IPCOUNTRY.get(dip) or {}).get('country', {}).get('iso_code', 'unknown')
175
169
  if dip_country == "IR":
176
170
  continue
@@ -7,7 +7,9 @@ from .adminlte import AdminLTEModelView
7
7
  from flask_babel import lazy_gettext as _
8
8
  from wtforms.validators import Regexp
9
9
  from flask_babel import gettext as __
10
- from flask import Markup, request # type: ignore
10
+ from flask import request # type: ignore
11
+ from markupsafe import Markup
12
+
11
13
  from flask import g
12
14
  import datetime
13
15
  from wtforms import SelectField
@@ -37,7 +39,7 @@ class SubAdminsField(SelectField):
37
39
 
38
40
  class AdminstratorAdmin(AdminLTEModelView):
39
41
  column_hide_backrefs = False
40
- column_list = ["name", 'UserLinks', 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'online_users', 'comment']
42
+ column_list = ["name", 'UserLinks', 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'online_users', 'comment',]
41
43
  form_columns = ["name", 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'comment', "uuid"]
42
44
  list_template = 'model/admin_list.html'
43
45
  # column_editable_list = ['name']
@@ -208,13 +210,14 @@ class AdminstratorAdmin(AdminLTEModelView):
208
210
 
209
211
  if g.account.mode != AdminMode.super_admin and model.mode == AdminMode.super_admin:
210
212
  raise ValidationError("Sub-Admin can not have more power!!!!")
211
- if model.mode == AdminMode.agent and model.mode != AdminMode.agent:
213
+ if g.account.mode == AdminMode.agent and model.mode != AdminMode.agent:
212
214
  raise ValidationError("Sub-Admin can not have more power!!!!")
213
215
 
214
216
  def on_model_delete(self, model):
215
217
  model.remove()
216
218
 
217
219
  def get_query_for_parent_admin(self):
220
+ # WHAT IS THIS?
218
221
  admin_user_id = self.get_pk_value()
219
222
  sub_admins_ids = set(recursive_sub_admins_ids(AdminUser.query.get(admin_user_id)))
220
223
  return AdminUser.query.filter(AdminUser.id.in_(sub_admins_ids)).with_entities(AdminUser.id, AdminUser.name)
@@ -236,3 +239,11 @@ class AdminstratorAdmin(AdminLTEModelView):
236
239
  del form.max_users
237
240
  del form.max_active_users
238
241
  del form.can_add_admin
242
+
243
+ def after_model_change(self, form, model, is_created):
244
+ if hutils.node.is_parent():
245
+ hutils.node.parent.send_sync_req_to_childs()
246
+
247
+ def after_model_delete(self, model):
248
+ if hutils.node.is_parent():
249
+ hutils.node.parent.send_sync_req_to_childs()
@@ -18,9 +18,9 @@ class Dashboard(FlaskView):
18
18
 
19
19
  @login_required(roles={Role.super_admin, Role.admin, Role.agent})
20
20
  def index(self):
21
-
22
21
  if hconfig(ConfigEnum.first_setup):
23
22
  return redirect(hurl_for("admin.QuickSetup:index"))
23
+
24
24
  if hiddifypanel.__release_date__ + datetime.timedelta(days=20) < datetime.datetime.now():
25
25
  hutils.flask.flash(_('This version of hiddify panel is outdated. Please update it from admin area.'), "danger") # type: ignore
26
26
  bot = None
@@ -34,20 +34,15 @@ class Dashboard(FlaskView):
34
34
  user_query = User.query
35
35
  if admin_id:
36
36
  user_query = user_query.filter(User.added_by == admin_id)
37
- if hconfig(ConfigEnum.is_parent):
37
+ if hutils.node.is_parent():
38
38
  childs = Child.query.filter(Child.id != 0).all()
39
39
  for c in childs:
40
40
  c.is_active = False
41
41
  for d in c.domains:
42
- if d.mode == DomainType.fake:
43
- continue
44
- remote = hiddify.get_account_panel_link(g.account, d.domain, child_id=c.id)
45
- d.is_active = hutils.network.check_connection_to_remote(remote)
42
+ d.is_active = hutils.node.parent.is_child_domain_active(c, d)
46
43
  if d.is_active:
47
44
  c.is_active = True
48
45
 
49
- # return render_template('parent_dash.html',childs=childs,bot=bot)
50
- # try:
51
46
  def_user = None if len(User.query.all()) > 1 else User.query.filter(User.name == 'default').first()
52
47
  domains = get_panel_domains()
53
48
  sslip_domains = [d.domain for d in domains if "sslip.io" in d.domain]
@@ -56,13 +51,13 @@ class Dashboard(FlaskView):
56
51
  quick_setup = hurl_for("admin.QuickSetup:index")
57
52
  hutils.flask.flash((_('It seems that you have not setup the system completely. <a class="btn btn-success" href="%(quick_setup)s">Click here</a> to complete setup.',
58
53
  quick_setup=quick_setup)), 'warning') # type: ignore
59
- if hconfig(ConfigEnum.is_parent):
54
+ if hutils.node.is_parent():
60
55
  hutils.flask.flash(
61
56
  _("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
62
57
  elif len(sslip_domains):
63
58
  hutils.flask.flash((_('It seems that you are using default domain (%(domain)s) which is not recommended.',
64
59
  domain=sslip_domains[0])), 'warning') # type: ignore
65
- if hconfig(ConfigEnum.is_parent):
60
+ if hutils.node.is_parent():
66
61
  hutils.flask.flash(
67
62
  _("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
68
63
  elif def_user:
@@ -3,7 +3,9 @@ from hiddifypanel.auth import login_required, current_account
3
3
 
4
4
  from hiddifypanel.models import *
5
5
  import re
6
- from flask import Markup, g # type: ignore
6
+ from flask import g # type: ignore
7
+ from markupsafe import Markup
8
+
7
9
  from flask_babel import gettext as __
8
10
  from flask_babel import lazy_gettext as _
9
11
  from hiddifypanel.panel.run_commander import Command, commander
@@ -169,26 +171,25 @@ class DomainAdmin(AdminLTEModelView):
169
171
  if hconfig(ConfigEnum.cloudflare) and model.mode != DomainType.fake:
170
172
  try:
171
173
  proxied = model.mode in [DomainType.cdn, DomainType.auto_cdn_ip]
172
- cf_api.add_or_update_domain(model.domain, ipv4_list[0], "A", proxied=proxied)
174
+ cf_api.add_or_update_domain(model.domain, str(ipv4_list[0]), "A", proxied=proxied)
173
175
  if ipv6_list:
174
- cf_api.add_or_update_domain(model.domain, ipv6_list[0], "AAAA", proxied=proxied)
176
+ cf_api.add_or_update_domain(model.domain, str(ipv6_list[0]), "AAAA", proxied=proxied)
175
177
 
176
178
  skip_check = True
177
179
  except Exception as e:
178
- # raise e
179
180
  raise ValidationError(__("Can not connect to Cloudflare.") + f' {e}')
180
181
  # elif model.mode==DomainType.auto_cdn_ip:
181
- if model.alias and not model.alias.replace("_", "").isalnum():
182
- hutils.flask.flash(__("Using alias with special charachters may cause problem in some clients like FairVPN."), 'warning')
183
- # raise ValidationError(_("You have to add your cloudflare api key to use this feature: "))
182
+ # if model.alias and not model.alias.replace("_", "").isalnum():
183
+ # hutils.flask.flash(__("Using alias with special charachters may cause problem in some clients like FairVPN."), 'warning')
184
+ # raise ValidationError(_("You have to add your cloudflare api key to use this feature: "))
184
185
 
185
186
  dip = hutils.network.get_domain_ip(model.domain)
186
187
  if model.sub_link_only:
187
188
  if dip is None:
188
- raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
189
+ raise ValidationError(_("Domain can not be resolved! there is a problem in your domain")) # type: ignore
189
190
  elif not skip_check:
190
191
  if dip is None:
191
- raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
192
+ raise ValidationError(_("Domain can not be resolved! there is a problem in your domain")) # type: ignore
192
193
 
193
194
  domain_ip_is_same_as_panel = False
194
195
  domain_ip_is_same_as_panel |= dip in ipv4_list
@@ -196,65 +197,52 @@ class DomainAdmin(AdminLTEModelView):
196
197
  domain_ip_is_same_as_panel |= ipaddress.ip_address(dip) == ipaddress.ip_address(ipv6)
197
198
 
198
199
  if model.mode == DomainType.direct and not domain_ip_is_same_as_panel:
199
- hutils.flask.flash(
200
- __(f"Domain IP={dip} is not matched with your ip={', '.join(list(map(str, ipv4_list)))} which is required in direct mode"),
201
- category='warning')
202
- # raise ValidationError(_("Domain IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode", server_ip=myip, domain_ip=dip))
200
+ # hutils.flask.flash(__(f"Domain IP={dip} is not matched with your ip={', '.join(list(map(str, ipv4_list)))} which is required in direct mode"),category='error')
201
+ raise ValidationError(
202
+ __("Domain IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode", server_ip=', '.join(list(map(str, ipv4_list))), domain_ip=dip)) # type: ignore
203
203
 
204
204
  if domain_ip_is_same_as_panel and model.mode in [DomainType.cdn, DomainType.relay, DomainType.fake, DomainType.auto_cdn_ip]:
205
- hutils.flask.flash(__(f"In CDN mode, Domain IP={dip} should be different to your ip={', '.join(list(map(str, ipv4_list)))}"), 'warning')
206
- # raise ValidationError(_("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s", server_ip=myip, domain_ip=dip))
205
+ # # hutils.flask.flash(__(f"In CDN mode, Domain IP={dip} should be different to your ip={', '.join(list(map(str, ipv4_list)))}"), 'warning')
206
+ raise ValidationError(__("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s",
207
+ server_ip=', '.join(list(map(str, ipv4_list))), domain_ip=dip)) # type: ignore
207
208
 
208
209
  # if model.mode in [DomainType.ss_faketls, DomainType.telegram_faketls]:
209
210
  # if len(Domain.query.filter(Domain.mode==model.mode and Domain.id!=model.id).all())>0:
210
211
  # ValidationError(f"another {model.mode} is exist")
212
+
211
213
  model.domain = model.domain.lower()
212
214
  if model.mode == DomainType.direct and model.cdn_ip:
213
215
  raise ValidationError(f"Specifying CDN IP is only valid for CDN mode")
214
216
 
215
217
  if model.mode == DomainType.fake and not model.cdn_ip:
216
- model.cdn_ip = ipv4_list[0]
218
+ model.cdn_ip = str(ipv4_list[0])
217
219
 
218
220
  # if model.mode==DomainType.fake and model.cdn_ip!=myip:
219
221
  # raise ValidationError(f"Specifying CDN IP is only valid for CDN mode")
220
222
 
221
- # work_with_ids = form.work_with.data
222
- # print(work_with_ids)
223
223
  # # Update the many-to-many relationship
224
224
  if len(model.show_domains) == Domain.query.count():
225
225
  model.show_domains = []
226
- # if model.alias and not g.is_commercial:
227
- # model.alias= "@hiddify "+model.alias
228
- # model.work_with = self.session.query(Domain).filter(
229
- # Domain.id.in_(work_with_ids)).all()
230
226
 
231
227
  if model.mode == DomainType.reality:
232
- model.servernames = (model.domain).lower()
233
- if not hutils.network.is_domain_reality_friendly(model.domain):
234
- # hutils.flask.flash(_("Domain is not REALITY friendly!")+" "+d,'error')
235
- # return render_template('config.html', form=form)
236
- raise ValidationError(_("Domain is not REALITY friendly!") + " " + model.domain)
237
-
238
- hiddify.debug_flash_if_not_in_the_same_asn(model.domain)
239
- if False:
240
228
  model.servernames = (model.servernames or model.domain).lower()
241
-
242
- for v in [model.domain, model.servernames]:
243
-
229
+ for v in set([model.domain, model.servernames]):
244
230
  for d in v.split(","):
245
231
  if not d:
246
232
  continue
247
-
248
233
  if not hutils.network.is_domain_reality_friendly(d):
249
- # hutils.flask.flash(_("Domain is not REALITY friendly!")+" "+d,'error')
250
- # return render_template('config.html', form=form)
251
- raise ValidationError(_("Domain is not REALITY friendly!") + " " + d)
234
+ raise ValidationError(_("Domain is not REALITY friendly!") + f' {d}')
252
235
 
253
- hiddify.debug_flash_if_not_in_the_same_asn(d)
236
+ if not hutils.network.is_in_same_asn(d, ipv4_list[0]):
237
+ server_asn = hutils.network.get_ip_asn_name(ipv4_list[0])
238
+ domain_asn = hutils.network.get_ip_asn_name(dip) # type: ignore
239
+ msg = _("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.") + \
240
+ (f"<br> Server ASN={server_asn}<br>{d}_ASN={domain_asn}" if server_asn or domain_asn else "")
241
+ hutils.flask.flash(msg, 'warning')
254
242
 
255
243
  for d in model.servernames.split(","):
256
- if not hiddify.fallback_domain_compatible_with_servernames(model.domain, d):
257
- raise ValidationError(_("REALITY Fallback domain is not compaitble with server names!") + " " + d + " != " + model.domain)
244
+ if not hutils.network.fallback_domain_compatible_with_servernames(model.domain, d):
245
+ raise ValidationError(_("REALITY Fallback domain is not compaitble with server names!") + f' {d} != {model.domain}')
258
246
 
259
247
  if (model.cdn_ip):
260
248
  try:
@@ -280,19 +268,18 @@ class DomainAdmin(AdminLTEModelView):
280
268
  hutils.flask.flash_config_success(restart_mode=ApplyMode.apply, domain_changed=True)
281
269
 
282
270
  def after_model_delete(self, model):
283
- # if hconfig(ConfigEnum.parent_panel):
284
- # hiddify_api.sync_child_to_parent()
285
- pass
271
+ if hutils.node.is_child():
272
+ if not hutils.node.child.sync_with_parent():
273
+ hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
286
274
 
287
275
  def after_model_change(self, form, model, is_created):
288
276
  if hconfig(ConfigEnum.first_setup):
289
277
  set_hconfig(ConfigEnum.first_setup, False)
290
- # if hconfig(ConfigEnum.parent_panel):
291
- # hiddify_api.sync_child_to_parent()
292
278
  if model.need_valid_ssl:
293
- # hiddify.exec_command(f"sudo /opt/hiddify-manager/acme.sh/get_cert.sh {model.domain}")
294
- # run get_cert.sh
295
279
  commander(Command.get_cert, domain=model.domain)
280
+ if hutils.node.is_child():
281
+ if not hutils.node.child.sync_with_parent():
282
+ hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
296
283
 
297
284
  def is_accessible(self):
298
285
  if login_required(roles={Role.super_admin, Role.admin})(lambda: True)() != True:
@@ -310,4 +297,4 @@ class DomainAdmin(AdminLTEModelView):
310
297
 
311
298
  def get_query(self):
312
299
  query = super().get_query()
313
- return query.filter(Domain.child_id == Child.current.id)
300
+ return query.filter(Domain.child_id == Child.current().id)
@@ -4,7 +4,9 @@ from wtforms.validators import Regexp, ValidationError
4
4
  from flask_babel import lazy_gettext as _
5
5
  from .adminlte import AdminLTEModelView
6
6
  from flask_babel import gettext as __
7
- from flask import Markup, g, request
7
+ from flask import g, request
8
+ from markupsafe import Markup
9
+
8
10
 
9
11
  from hiddifypanel.auth import login_required
10
12
  from hiddifypanel.panel import hiddify
@@ -40,7 +42,7 @@ class NodeAdmin(AdminLTEModelView):
40
42
  def is_accessible(self):
41
43
  if login_required(roles={Role.super_admin})(lambda: True)() != True:
42
44
  return False
43
- if Child.current.id != 0:
45
+ if Child.current().id != 0:
44
46
  return False
45
47
  return True
46
48
 
@@ -49,7 +51,9 @@ class NodeAdmin(AdminLTEModelView):
49
51
  raise ValidationError(_("Remote nodes are not supported yet!"))
50
52
 
51
53
  def after_model_change(self, form, model, is_created):
54
+ # deprecated
52
55
  set_hconfig(ConfigEnum.is_parent, True)
56
+ set_hconfig(ConfigEnum.panel_mode, PanelMode.parent)
53
57
  if is_created and model.mode == ChildMode.virtual:
54
58
  # for k, v in get_hconfigs().items():
55
59
  # set_hconfig(k, v, model.id)
@@ -35,7 +35,7 @@ class ProxyAdmin(FlaskView):
35
35
 
36
36
  db.session.commit()
37
37
  # print(cat,vs)
38
- hiddify.get_available_proxies.invalidate_all()
38
+ hutils.proxy.get_proxies.invalidate_all()
39
39
  hiddify.check_need_reset(old_configs)
40
40
  all_proxy_form = get_all_proxy_form(True)
41
41
 
@@ -55,10 +55,11 @@ class ProxyAdmin(FlaskView):
55
55
 
56
56
  # print(cat,vs)
57
57
  db.session.commit()
58
- hiddify.get_available_proxies.invalidate_all()
58
+ hutils.proxy.get_proxies.invalidate_all()
59
+ if hutils.node.is_child():
60
+ if not hutils.node.child.sync_with_parent():
61
+ hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
59
62
  hutils.flask.flash_config_success(restart_mode=ApplyMode.apply, domain_changed=False)
60
- # if hconfig(ConfigEnum.parent_panel):
61
- # hiddify_api.sync_child_to_parent()
62
63
  global_config_form = get_global_config_form(True)
63
64
  else:
64
65
  hutils.flask.flash((_('config.validation-error')), 'danger')
@@ -94,7 +95,7 @@ def get_global_config_form(empty=False):
94
95
 
95
96
 
96
97
  def get_all_proxy_form(empty=False):
97
- proxies = hiddify.get_available_proxies(Child.current.id)
98
+ proxies = hutils.proxy.get_proxies(Child.current().id)
98
99
  categories1 = sorted([c for c in {c.cdn: 1 for c in proxies}])
99
100
 
100
101
  class DynamicForm(FlaskForm):
@@ -8,7 +8,6 @@ import wtforms as wtf
8
8
  from flask_wtf import FlaskForm
9
9
  from flask_bootstrap import SwitchField
10
10
  from hiddifypanel.panel import hiddify
11
- from wtforms.fields import *
12
11
  from flask_classful import FlaskView
13
12
  from wtforms.validators import ValidationError
14
13
  # from gettext import gettext as _
@@ -61,7 +60,7 @@ class QuickSetup(FlaskView):
61
60
  Domain.query.filter(Domain.domain == f'{hutils.network.get_ip_str(4)}.sslip.io').delete()
62
61
  db.session.add(Domain(domain=quick_form.domain.data.lower(), mode=DomainType.direct))
63
62
  set_hconfig(ConfigEnum.block_iran_sites, quick_form.block_iran_sites.data)
64
- set_hconfig(ConfigEnum.decoy_domain,quick_form.decoy_domain.data)
63
+ set_hconfig(ConfigEnum.decoy_domain, quick_form.decoy_domain.data)
65
64
  # hiddify.bulk_register_configs([
66
65
  # # {"key": ConfigEnum.telegram_enable, "value": quick_form.enable_telegram.data == True},
67
66
  # # {"key": ConfigEnum.vmess_enable, "value": quick_form.enable_vmess.data == True},
@@ -84,12 +83,16 @@ class QuickSetup(FlaskView):
84
83
 
85
84
  def get_lang_form(empty=False):
86
85
  class LangForm(FlaskForm):
87
- admin_lang = wtf.fields.SelectField(_("config.admin_lang.label"), choices=[("en", _("lang.en")), ("fa", _("lang.fa")), ("pt", _(
88
- "lang.pt")), ("zh", _("lang.zh")), ("ru", _("lang.ru"))], description=_("config.admin_lang.description"), default=hconfig(ConfigEnum.admin_lang))
89
- # lang=wtf.fields.SelectField(_("config.lang.label"),choices=[("en",_("lang.en")),("fa",_("lang.fa"))],description=_("config.lang.description"),default=hconfig(ConfigEnum.lang))
90
- country = wtf.fields.SelectField(_("config.country.label"), choices=[("ir", _("Iran")), ("zh", _(
91
- "China")), ("other", "Others")], description=_("config.country.description"), default=hconfig(ConfigEnum.country))
92
- lang_submit = wtf.fields.SubmitField(_('Submit'))
86
+ admin_lang = wtf.SelectField(
87
+ _("config.admin_lang.label"), choices=[("en", _("lang.en")), ("fa", _("lang.fa")), ("pt", _("lang.pt")), ("zh", _("lang.zh")), ("ru", _("lang.ru"))],
88
+ description=_("config.admin_lang.description"),
89
+ default=hconfig(ConfigEnum.admin_lang))
90
+ # lang=wtf.SelectField(_("config.lang.label"),choices=[("en",_("lang.en")),("fa",_("lang.fa"))],description=_("config.lang.description"),default=hconfig(ConfigEnum.lang))
91
+ country = wtf.SelectField(
92
+ _("config.country.label"), choices=[("ir", _("Iran")), ("zh", _("China")), ("other", "Others")],
93
+ description=_("config.country.description"),
94
+ default=hconfig(ConfigEnum.country))
95
+ lang_submit = wtf.SubmitField(_('Submit'))
93
96
 
94
97
  return LangForm(None)if empty else LangForm()
95
98
 
@@ -108,13 +111,12 @@ def get_quick_setup_form(empty=False):
108
111
  class QuickSetupForm(FlaskForm):
109
112
  domain_regex = "^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})$"
110
113
 
111
- domain_validators = [wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.Invalid domain")),
112
- validate_domain,
113
- wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.Domain already used")),
114
- wtf.validators.NoneOf([c.value.lower() for c in StrConfig.query.all() if "fakedomain" in c.key and c.key !=
115
- ConfigEnum.decoy_domain], _("config.Domain already used"))
116
- ]
117
- domain = wtf.fields.StringField(
114
+ domain_validators = [
115
+ wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.Invalid domain")),
116
+ validate_domain,
117
+ wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.Domain already used")),
118
+ wtf.validators.NoneOf([c.value.lower() for c in StrConfig.query.all() if "fakedomain" in c.key and c.key != ConfigEnum.decoy_domain], _("config.Domain already used"))]
119
+ domain = wtf.StringField(
118
120
  _("domain.domain"),
119
121
  domain_validators,
120
122
  description=_("domain.description"),
@@ -129,9 +131,9 @@ def get_quick_setup_form(empty=False):
129
131
  block_iran_sites = SwitchField(_("config.block_iran_sites.label"), description=_(
130
132
  "config.block_iran_sites.description"), default=hconfig(ConfigEnum.block_iran_sites))
131
133
  # enable_vmess = SwitchField(_("config.vmess_enable.label"), description=_("config.vmess_enable.description"), default=hconfig(ConfigEnum.vmess_enable))
132
- decoy_domain = wtf.fields.StringField(_("config.decoy_domain.label"), description=_("config.decoy_domain.description"), default=hconfig(
133
- ConfigEnum.decoy_domain), validators=[wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.Invalid domain")), hiddify.validate_domain_exist])
134
- submit = wtf.fields.SubmitField(_('Submit'))
134
+ decoy_domain = wtf.StringField(_("config.decoy_domain.label"), description=_("config.decoy_domain.description"), default=hconfig(
135
+ ConfigEnum.decoy_domain), validators=[wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.Invalid domain")), hutils.flask.validate_domain_exist])
136
+ submit = wtf.SubmitField(_('Submit'))
135
137
 
136
138
  return QuickSetupForm(None) if empty else QuickSetupForm()
137
139
 
@@ -144,8 +146,7 @@ def validate_domain(form, field):
144
146
 
145
147
  myip = hutils.network.get_ip(4)
146
148
  if dip and myip != dip:
147
- raise ValidationError(_("Domain (%(domain)s)-> IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode",
148
- server_ip=myip, domain_ip=dip, domain=domain))
149
+ raise ValidationError(_("Domain (%(domain)s)-> IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode", server_ip=myip, domain_ip=dip, domain=domain))
149
150
 
150
151
 
151
152
  def admin_link():