hiddifypanel 10.10.20__py3-none-any.whl → 10.11.1__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 (68) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/hutils/__init__.py +3 -0
  4. hiddifypanel/hutils/crypto.py +29 -0
  5. hiddifypanel/hutils/encode.py +4 -0
  6. hiddifypanel/hutils/flask.py +4 -0
  7. hiddifypanel/hutils/github_issue.py +1 -1
  8. hiddifypanel/{models/utils.py → hutils/model.py} +14 -4
  9. hiddifypanel/hutils/network/net.py +46 -2
  10. hiddifypanel/hutils/proxy/__init__.py +4 -0
  11. hiddifypanel/hutils/proxy/clash.py +161 -0
  12. hiddifypanel/hutils/proxy/shared.py +414 -0
  13. hiddifypanel/hutils/proxy/singbox.py +338 -0
  14. hiddifypanel/hutils/proxy/xray.py +561 -0
  15. hiddifypanel/models/admin.py +16 -14
  16. hiddifypanel/models/base_account.py +4 -4
  17. hiddifypanel/models/config.py +1 -1
  18. hiddifypanel/models/proxy.py +17 -17
  19. hiddifypanel/models/user.py +15 -13
  20. hiddifypanel/panel/admin/AdminstratorAdmin.py +1 -1
  21. hiddifypanel/panel/admin/DomainAdmin.py +13 -27
  22. hiddifypanel/panel/admin/ProxyAdmin.py +3 -3
  23. hiddifypanel/panel/admin/SettingAdmin.py +22 -11
  24. hiddifypanel/panel/admin/UserAdmin.py +9 -7
  25. hiddifypanel/panel/cf_api.py +1 -2
  26. hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +5 -6
  27. hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +1 -1
  28. hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +4 -7
  29. hiddifypanel/panel/common.py +5 -3
  30. hiddifypanel/panel/hiddify.py +0 -90
  31. hiddifypanel/panel/init_db.py +14 -9
  32. hiddifypanel/panel/user/__init__.py +0 -1
  33. hiddifypanel/panel/user/templates/all_configs copy.txt +2 -2
  34. hiddifypanel/panel/user/templates/all_configs.txt +2 -2
  35. hiddifypanel/panel/user/templates/base_singbox_config.json.j2 +2 -1
  36. hiddifypanel/panel/user/templates/base_xray_config.json.j2 +125 -0
  37. hiddifypanel/panel/user/templates/clash_config copy.yml +1 -1
  38. hiddifypanel/panel/user/templates/clash_config.yml +4 -4
  39. hiddifypanel/panel/user/templates/clash_proxies.yml +1 -1
  40. hiddifypanel/panel/user/templates/home/all-configs.html +2 -2
  41. hiddifypanel/panel/user/templates/home/all-configs_old.html +1 -1
  42. hiddifypanel/panel/user/templates/home/ios copy.html +2 -2
  43. hiddifypanel/panel/user/user.py +50 -44
  44. hiddifypanel/templates/admin-layout.html +16 -24
  45. hiddifypanel/templates/fake.html +0 -276
  46. hiddifypanel/templates/flaskadmin-layout.html +2 -1
  47. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  48. hiddifypanel/translations/en/LC_MESSAGES/messages.po +16 -9
  49. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  50. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +12 -6
  51. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  52. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +9 -4
  53. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  54. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +12 -7
  55. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  56. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +9 -4
  57. hiddifypanel/translations.i18n/en.json +8 -7
  58. hiddifypanel/translations.i18n/fa.json +5 -4
  59. hiddifypanel/translations.i18n/pt.json +3 -2
  60. hiddifypanel/translations.i18n/ru.json +6 -5
  61. hiddifypanel/translations.i18n/zh.json +3 -2
  62. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/METADATA +1 -1
  63. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/RECORD +67 -61
  64. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/WHEEL +1 -1
  65. hiddifypanel/panel/user/link_maker.py +0 -1089
  66. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/LICENSE.md +0 -0
  67. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/entry_points.txt +0 -0
  68. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
- from enum import auto
2
-
3
1
  from sqlalchemy_serializer import SerializerMixin
4
2
  from strenum import StrEnum
3
+ from enum import auto
4
+ from sqlalchemy import Column, String, Integer, Boolean, Enum, ForeignKey
5
5
 
6
6
  from hiddifypanel.database import db
7
7
 
@@ -57,15 +57,15 @@ class ProxyL3(StrEnum):
57
57
  custom = auto()
58
58
 
59
59
 
60
- class Proxy(db.Model, SerializerMixin):
61
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
62
- child_id = db.Column(db.Integer, db.ForeignKey('child.id'), default=0)
63
- name = db.Column(db.String(200), nullable=False, unique=False)
64
- enable = db.Column(db.Boolean, nullable=False)
65
- proto = db.Column(db.Enum(ProxyProto), nullable=False)
66
- l3 = db.Column(db.Enum(ProxyL3), nullable=False)
67
- transport = db.Column(db.Enum(ProxyTransport), nullable=False)
68
- cdn = db.Column(db.Enum(ProxyCDN), nullable=False)
60
+ class Proxy(db.Model, SerializerMixin): # type: ignore
61
+ id = Column(Integer, primary_key=True, autoincrement=True)
62
+ child_id = Column(Integer, ForeignKey('child.id'), default=0)
63
+ name = Column(String(200), nullable=False, unique=False)
64
+ enable = Column(Boolean, nullable=False)
65
+ proto = Column(Enum(ProxyProto), nullable=False)
66
+ l3 = Column(Enum(ProxyL3), nullable=False)
67
+ transport = Column(Enum(ProxyTransport), nullable=False)
68
+ cdn = Column(Enum(ProxyCDN), nullable=False)
69
69
 
70
70
  @property
71
71
  def enabled(self):
@@ -82,12 +82,15 @@ class Proxy(db.Model, SerializerMixin):
82
82
  'child_unique_id': self.child.unique_id if self.child else ''
83
83
  }
84
84
 
85
+ def __str__(self):
86
+ return str(self.to_dict())
87
+
85
88
  @staticmethod
86
89
  def add_or_update(commit=True, child_id=0, **proxy):
87
90
  dbproxy = Proxy.query.filter(Proxy.name == proxy['name']).first()
88
91
  if not dbproxy:
89
92
  dbproxy = Proxy()
90
- db.session.add(dbproxy)
93
+ db.session.add(dbproxy) # type: ignore
91
94
  dbproxy.enable = proxy['enable']
92
95
  dbproxy.name = proxy['name']
93
96
  dbproxy.proto = proxy['proto']
@@ -96,7 +99,7 @@ class Proxy(db.Model, SerializerMixin):
96
99
  dbproxy.l3 = proxy['l3']
97
100
  dbproxy.child_id = child_id
98
101
  if commit:
99
- db.session.commit()
102
+ db.session.commit() # type: ignore
100
103
 
101
104
  @staticmethod
102
105
  def bulk_register(proxies, commit=True, override_child_unique_id=None):
@@ -105,7 +108,4 @@ class Proxy(db.Model, SerializerMixin):
105
108
  child_id = hiddify.get_child(unique_id=None)
106
109
  Proxy.add_or_update(commit=False, child_id=child_id, **proxy)
107
110
  if commit:
108
- db.session.commit()
109
-
110
- def __str__(self):
111
- return str(self.to_dict())
111
+ db.session.commit() # type: ignore
@@ -8,7 +8,6 @@ from sqlalchemy import event
8
8
 
9
9
  from hiddifypanel.database import db
10
10
  from hiddifypanel.models import Lang
11
- from hiddifypanel.models.utils import fill_password, fill_username
12
11
  from hiddifypanel.models.base_account import BaseAccount
13
12
  from hiddifypanel.models.admin import AdminUser
14
13
 
@@ -208,7 +207,7 @@ class User(BaseAccount):
208
207
  from hiddifypanel import hutils
209
208
  dbuser = super().add_or_update(commit=commit, **data)
210
209
  if data.get('added_by_uuid'):
211
- admin = AdminUser.by_uuid(data.get('added_by_uuid'), create=True) or AdminUser.current_admin_or_owner()
210
+ admin = AdminUser.by_uuid(data.get('added_by_uuid'), create=True) or AdminUser.current_admin_or_owner() # type: ignore
212
211
  dbuser.added_by = admin.id
213
212
  else:
214
213
  dbuser.added_by = 1
@@ -233,17 +232,17 @@ class User(BaseAccount):
233
232
  if data.get('ed25519_private_key', ''):
234
233
  dbuser.ed25519_private_key = data.get('ed25519_private_key', '')
235
234
  dbuser.ed25519_public_key = data.get('ed25519_public_key', '')
236
- if not dbuser.ed25519_private_key:
237
- from hiddifypanel.panel import hiddify
238
- priv, publ = hiddify.get_ed25519_private_public_pair()
239
- dbuser.ed25519_private_key = priv
240
- dbuser.ed25519_public_key = publ
235
+ # if not dbuser.ed25519_private_key:
236
+ # priv, publ = hutils.crypto.get_ed25519_private_public_pair()
237
+ # dbuser.ed25519_private_key = priv
238
+ # dbuser.ed25519_public_key = publ
239
+
241
240
  dbuser.wg_pk = data.get('wg_pk', dbuser.wg_pk)
242
241
  dbuser.wg_pub = data.get('wg_pub', dbuser.wg_pub)
243
242
  dbuser.wg_psk = data.get('wg_psk', dbuser.wg_psk)
244
- if not dbuser.wg_pk:
245
- from hiddifypanel.panel import hiddify
246
- dbuser.wg_pk, dbuser.wg_pub, dbuser.wg_psk = hiddify.get_wg_private_public_psk_pair()
243
+
244
+ # if not dbuser.wg_pk:
245
+ # dbuser.wg_pk, dbuser.wg_pub, dbuser.wg_psk = hutils.crypto.get_wg_private_public_psk_pair()
247
246
 
248
247
  mode = data.get('mode', UserMode.no_reset)
249
248
  if mode == 'disable':
@@ -270,7 +269,7 @@ class User(BaseAccount):
270
269
  'start_date': hutils.convert.date_to_json(self.start_date)if convert_date else self.start_date,
271
270
  'current_usage_GB': self.current_usage_GB,
272
271
  'last_reset_time': hutils.convert.date_to_json(self.last_reset_time) if convert_date else self.last_reset_time,
273
- 'added_by_uuid': self.admin.uuid,
272
+ 'added_by_uuid': self.admin.uuid if self.admin else None,
274
273
  'ed25519_private_key': self.ed25519_private_key,
275
274
  'ed25519_public_key': self.ed25519_public_key,
276
275
  'wg_pk': self.wg_pk,
@@ -302,5 +301,8 @@ class User(BaseAccount):
302
301
 
303
302
  @event.listens_for(User, 'before_insert')
304
303
  def on_user_insert(mapper, connection, target):
305
- fill_username(target)
306
- fill_password(target)
304
+ from hiddifypanel import hutils
305
+ hutils.model.gen_username(target)
306
+ hutils.model.gen_password(target)
307
+ hutils.model.gen_ed25519_keys(target)
308
+ hutils.model.gen_wg_keys(target)
@@ -208,7 +208,7 @@ class AdminstratorAdmin(AdminLTEModelView):
208
208
 
209
209
  if g.account.mode != AdminMode.super_admin and model.mode == AdminMode.super_admin:
210
210
  raise ValidationError("Sub-Admin can not have more power!!!!")
211
- if model.mode == AdminMode.agent and model.mode != AdminMode.agent:
211
+ if g.account.mode == AdminMode.agent and model.mode != AdminMode.agent:
212
212
  raise ValidationError("Sub-Admin can not have more power!!!!")
213
213
 
214
214
  def on_model_delete(self, model):
@@ -199,9 +199,10 @@ class DomainAdmin(AdminLTEModelView):
199
199
  raise ValidationError(
200
200
  __("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
201
201
 
202
- # if domain_ip_is_same_as_panel and model.mode in [DomainType.cdn, DomainType.relay, DomainType.fake, DomainType.auto_cdn_ip]:
203
- # # hutils.flask.flash(__(f"In CDN mode, Domain IP={dip} should be different to your ip={', '.join(list(map(str, ipv4_list)))}"), 'warning')
204
- # raise ValidationError(__("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s", server_ip=', '.join(list(map(str, ipv4_list))), domain_ip=dip)) # type: ignore
202
+ if domain_ip_is_same_as_panel and model.mode in [DomainType.cdn, DomainType.relay, DomainType.fake, DomainType.auto_cdn_ip]:
203
+ # # hutils.flask.flash(__(f"In CDN mode, Domain IP={dip} should be different to your ip={', '.join(list(map(str, ipv4_list)))}"), 'warning')
204
+ raise ValidationError(__("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s",
205
+ server_ip=', '.join(list(map(str, ipv4_list))), domain_ip=dip)) # type: ignore
205
206
 
206
207
  # if model.mode in [DomainType.ss_faketls, DomainType.telegram_faketls]:
207
208
  # if len(Domain.query.filter(Domain.mode==model.mode and Domain.id!=model.id).all())>0:
@@ -217,43 +218,28 @@ class DomainAdmin(AdminLTEModelView):
217
218
  # if model.mode==DomainType.fake and model.cdn_ip!=myip:
218
219
  # raise ValidationError(f"Specifying CDN IP is only valid for CDN mode")
219
220
 
220
- # work_with_ids = form.work_with.data
221
- # print(work_with_ids)
222
221
  # # Update the many-to-many relationship
223
222
  if len(model.show_domains) == Domain.query.count():
224
223
  model.show_domains = []
225
- # if model.alias and not g.is_commercial:
226
- # model.alias= "@hiddify "+model.alias
227
- # model.work_with = self.session.query(Domain).filter(
228
- # Domain.id.in_(work_with_ids)).all()
229
224
 
230
225
  if model.mode == DomainType.reality:
231
- model.servernames = (model.domain).lower()
232
- if not hutils.network.is_domain_reality_friendly(model.domain):
233
- # hutils.flask.flash(_("Domain is not REALITY friendly!")+" "+d,'error')
234
- # return render_template('config.html', form=form)
235
- raise ValidationError(_("Domain is not REALITY friendly!") + " " + model.domain)
236
-
237
- hiddify.debug_flash_if_not_in_the_same_asn(model.domain)
238
- if False:
239
226
  model.servernames = (model.servernames or model.domain).lower()
240
-
241
- for v in [model.domain, model.servernames]:
242
-
227
+ for v in set([model.domain, model.servernames]):
243
228
  for d in v.split(","):
244
229
  if not d:
245
230
  continue
246
-
247
231
  if not hutils.network.is_domain_reality_friendly(d):
248
- # hutils.flask.flash(_("Domain is not REALITY friendly!")+" "+d,'error')
249
- # return render_template('config.html', form=form)
250
- raise ValidationError(_("Domain is not REALITY friendly!") + " " + d)
232
+ raise ValidationError(_("Domain is not REALITY friendly!")+f' {d}')
251
233
 
252
- hiddify.debug_flash_if_not_in_the_same_asn(d)
234
+ if not hutils.network.is_in_same_asn(d, ipv4_list[0]):
235
+ server_asn = hutils.network.get_ip_asn_name(ipv4_list[0])
236
+ domain_asn = hutils.network.get_ip_asn_name(dip) # type: ignore
237
+ 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.")+(f"<br> Server ASN={server_asn}<br>{d}_ASN={domain_asn}" if server_asn or domain_asn else "")
238
+ hutils.flask.flash(msg, 'warning')
253
239
 
254
240
  for d in model.servernames.split(","):
255
- if not hiddify.fallback_domain_compatible_with_servernames(model.domain, d):
256
- raise ValidationError(_("REALITY Fallback domain is not compaitble with server names!") + " " + d + " != " + model.domain)
241
+ if not hutils.network.fallback_domain_compatible_with_servernames(model.domain, d):
242
+ raise ValidationError(_("REALITY Fallback domain is not compaitble with server names!")+f' {d} != {model.domain}')
257
243
 
258
244
  if (model.cdn_ip):
259
245
  try:
@@ -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,7 +55,7 @@ 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
59
  hutils.flask.flash_config_success(restart_mode=ApplyMode.apply, domain_changed=False)
60
60
  # if hconfig(ConfigEnum.parent_panel):
61
61
  # hiddify_api.sync_child_to_parent()
@@ -94,7 +94,7 @@ def get_global_config_form(empty=False):
94
94
 
95
95
 
96
96
  def get_all_proxy_form(empty=False):
97
- proxies = hiddify.get_available_proxies(Child.current.id)
97
+ proxies = hutils.proxy.get_proxies(Child.current.id)
98
98
  categories1 = sorted([c for c in {c.cdn: 1 for c in proxies}])
99
99
 
100
100
  class DynamicForm(FlaskForm):
@@ -122,7 +122,6 @@ class SettingAdmin(FlaskView):
122
122
 
123
123
 
124
124
  def get_config_form():
125
-
126
125
  strconfigs = StrConfig.query.filter(StrConfig.child_id == Child.current.id).all()
127
126
  boolconfigs = BoolConfig.query.filter(BoolConfig.child_id == Child.current.id).all()
128
127
  bool_types = {c.key: 'bool' for c in boolconfigs}
@@ -154,7 +153,8 @@ def get_config_form():
154
153
  if c.key in bool_types:
155
154
  field = SwitchField(_(f'config.{c.key}.label'), default=c.value, description=_(f'config.{c.key}.description'))
156
155
  elif c.key == ConfigEnum.core_type:
157
- field = wtf.SelectField(_(f"config.{c.key}.label"), choices=[("xray", _("Xray")), ("singbox", _("SingBox"))], description=_(f"config.{c.key}.description"), default=hconfig(c.key))
156
+ field = wtf.SelectField(_(f"config.{c.key}.label"), choices=[("xray", _("Xray")), ("singbox", _(
157
+ "SingBox"))], description=_(f"config.{c.key}.description"), default=hconfig(c.key))
158
158
  elif c.key == ConfigEnum.warp_mode:
159
159
  field = wtf.SelectField(
160
160
  _(f"config.{c.key}.label"), choices=[("disable", _("Disable")), ("all", _("All")), ("custom", _("Only Blocked and Local websites"))],
@@ -168,7 +168,8 @@ def get_config_form():
168
168
  description=_(f"config.{c.key}.description"),
169
169
  default=hconfig(c.key))
170
170
  elif c.key == ConfigEnum.country:
171
- field = wtf.SelectField(_(f"config.{c.key}.label"), choices=[("ir", _("Iran")), ("zh", _("China")), ("other", _("Others"))], description=_(f"config.{c.key}.description"), default=hconfig(c.key))
171
+ field = wtf.SelectField(_(f"config.{c.key}.label"), choices=[("ir", _("Iran")), ("zh", _(
172
+ "China")), ("other", _("Others"))], description=_(f"config.{c.key}.description"), default=hconfig(c.key))
172
173
  elif c.key == ConfigEnum.package_mode:
173
174
  package_modes = [("release", _("Release")), ("beta", _("Beta"))]
174
175
  if hconfig(c.key) == "develop":
@@ -193,9 +194,14 @@ def get_config_form():
193
194
  elif c.key == ConfigEnum.telegram_lib:
194
195
  # if hconfig(ConfigEnum.telegram_lib)=='python':
195
196
  # continue6
196
- libs = [("python", _("lib.telegram.python")), ("tgo", _("lib.telegram.go")),
197
- ("orig", _("lib.telegram.orignal")), ("erlang", _("lib.telegram.erlang"))]
198
- field = wtf.SelectField(_("config.telegram_lib.label"), choices=libs, description=_("config.telegram_lib.description"), default=hconfig(ConfigEnum.telegram_lib))
197
+ libs = [
198
+ ("erlang", _("lib.telegram.erlang")),
199
+ ("python", _("lib.telegram.python")),
200
+ ("tgo", _("lib.telegram.go")),
201
+ # ("orig", _("lib.telegram.orignal")),
202
+ ]
203
+ field = wtf.SelectField(_("config.telegram_lib.label"), choices=libs, description=_(
204
+ "config.telegram_lib.description"), default=hconfig(ConfigEnum.telegram_lib))
199
205
  elif c.key == ConfigEnum.mux_protocol:
200
206
  choices = [("smux", 'smux'), ("yamux", "yamux"), ("h2mux", "h2mux")]
201
207
  field = wtf.SelectField(_(f"config.{c.key}.label"), choices=choices, description=_(f"config.{c.key}.description"), default=hconfig(c.key))
@@ -203,11 +209,13 @@ def get_config_form():
203
209
  elif c.key == ConfigEnum.warp_sites:
204
210
  validators = [wtf.validators.Length(max=2048)]
205
211
  render_kw = {'class': "ltr", 'maxlength': 2048}
206
- field = wtf.TextAreaField(_(f'config.{c.key}.label'), validators, default=c.value, description=_(f'config.{c.key}.description'), render_kw=render_kw)
212
+ field = wtf.TextAreaField(_(f'config.{c.key}.label'), validators, default=c.value,
213
+ description=_(f'config.{c.key}.description'), render_kw=render_kw)
207
214
  elif c.key == ConfigEnum.branding_freetext:
208
215
  validators = [wtf.validators.Length(max=2048)]
209
216
  render_kw = {'class': "ltr", 'maxlength': 2048}
210
- field = custom_widgets.CKTextAreaField(_(f'config.{c.key}.label'), validators, default=c.value, description=_(f'config.{c.key}.description'), render_kw=render_kw)
217
+ field = custom_widgets.CKTextAreaField(_(f'config.{c.key}.label'), validators, default=c.value,
218
+ description=_(f'config.{c.key}.description'), render_kw=render_kw)
211
219
  else:
212
220
  render_kw = {'class': "ltr"}
213
221
  validators = []
@@ -239,11 +247,13 @@ def get_config_form():
239
247
  if c.key == ConfigEnum.telegram_bot_token:
240
248
  validators.append(wtf.validators.Regexp("()|^([0-9]{8,12}:[a-zA-Z0-9_-]{30,40})|$", re.IGNORECASE, _("config.Invalid telegram bot token")))
241
249
  if c.key == ConfigEnum.branding_site:
242
- validators.append(wtf.validators.Regexp("()|(http(s|)://([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})/?.*)", re.IGNORECASE, _("config.Invalid brand link")))
250
+ validators.append(wtf.validators.Regexp(
251
+ "()|(http(s|)://([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})/?.*)", re.IGNORECASE, _("config.Invalid brand link")))
243
252
  # render_kw['required']=""
244
253
 
245
254
  if 'secret' in c.key:
246
- validators.append(wtf.validators.Regexp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", re.IGNORECASE, _('config.invalid uuid')))
255
+ validators.append(wtf.validators.Regexp(
256
+ "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", re.IGNORECASE, _('config.invalid uuid')))
247
257
  render_kw['required'] = ""
248
258
 
249
259
  if c.key == ConfigEnum.proxy_path:
@@ -274,7 +284,8 @@ def get_config_form():
274
284
  if c.key == ConfigEnum.reality_public_key and g.account.mode in [AdminMode.super_admin]:
275
285
  extra_info = f" <a href='{hurl_for('admin.Actions:change_reality_keys')}'>{_('Change')}</a>"
276
286
 
277
- field = wtf.StringField(_(f'config.{c.key}.label'), validators, default=c.value, description=_(f'config.{c.key}.description') + extra_info, render_kw=render_kw)
287
+ field = wtf.StringField(_(f'config.{c.key}.label'), validators, default=c.value,
288
+ description=_(f'config.{c.key}.description') + extra_info, render_kw=render_kw)
278
289
  setattr(CategoryForm, f'{c.key}', field)
279
290
 
280
291
  multifield = wtf.FormField(CategoryForm, Markup('<i class="fa-solid fa-plus"></i>&nbsp' + _(f'config.{cat}.label')))
@@ -271,14 +271,16 @@ class UserAdmin(AdminLTEModelView):
271
271
  active=g.account.max_active_users, total=g.account.max_users))
272
272
  if old_user and old_user.uuid != model.uuid:
273
273
  user_driver.remove_client(old_user)
274
- if not model.ed25519_private_key:
275
- priv, publ = hiddify.get_ed25519_private_public_pair()
276
- model.ed25519_private_key = priv
277
- model.ed25519_public_key = publ
278
- if not model.wg_pk:
279
- model.wg_pk, model.wg_pub, model.wg_psk = hiddify.get_wg_private_public_psk_pair()
280
- # model.expiry_time=datetime.date.today()+datetime.timedelta(days=model.expiry_time)
281
274
 
275
+ # generated automatically
276
+ # if not model.ed25519_private_key:
277
+ # priv, publ = hutils.crypto.get_ed25519_private_public_pair()
278
+ # model.ed25519_private_key = priv
279
+ # model.ed25519_public_key = publ
280
+ # if not model.wg_pk:
281
+ # model.wg_pk, model.wg_pub, model.wg_psk = hutils.crypto.get_wg_private_public_psk_pair()
282
+
283
+ # model.expiry_time=datetime.date.today()+datetime.timedelta(days=model.expiry_time)
282
284
  # if model.current_usage_GB < model.usage_limit_GB:
283
285
  # xray_api.add_client(model.uuid)
284
286
  # else:
@@ -1,6 +1,5 @@
1
1
  import CloudFlare
2
-
3
- from hiddifypanel.models import *
2
+ from hiddifypanel.models import hconfig, ConfigEnum
4
3
 
5
4
 
6
5
  def add_or_update_domain(domain, ip, dns_type="A", proxied=True):
@@ -2,7 +2,6 @@ from hiddifypanel.models import *
2
2
  from hiddifypanel.panel.admin.adminlte import AdminLTEModelView
3
3
  from flask_babel import gettext as __
4
4
  from flask_babel import lazy_gettext as _
5
- from hiddifypanel.panel import hiddify
6
5
  from flask import g, redirect, Markup
7
6
  from hiddifypanel.hutils.flask import hurl_for, flash
8
7
  from hiddifypanel.auth import login_required
@@ -10,8 +9,8 @@ from flask_admin.model.template import EndpointLinkRowAction
10
9
  from flask_admin.actions import action
11
10
  from flask_admin.contrib.sqla import form, filters as sqla_filters, tools
12
11
  # Define a custom field type for the related domains
13
-
14
12
  from flask import current_app
13
+ from hiddifypanel import hutils
15
14
 
16
15
 
17
16
  class ProxyDetailsAdmin(AdminLTEModelView):
@@ -30,7 +29,7 @@ class ProxyDetailsAdmin(AdminLTEModelView):
30
29
 
31
30
  self.session.commit()
32
31
  flash(_('%(count)s records were successfully disabled.', count=count), 'success')
33
- hiddify.get_available_proxies.invalidate_all()
32
+ hutils.proxy.get_proxies.invalidate_all()
34
33
 
35
34
  @action('enable', 'Enable', 'Are you sure you want to enable selected proxies?')
36
35
  def action_enable(self, ids):
@@ -39,7 +38,7 @@ class ProxyDetailsAdmin(AdminLTEModelView):
39
38
 
40
39
  self.session.commit()
41
40
  flash(_('%(count)s records were successfully enabled.', count=count), 'success')
42
- hiddify.get_available_proxies.invalidate_all()
41
+ hutils.proxy.get_proxies.invalidate_all()
43
42
 
44
43
  # list_template = 'model/domain_list.html'
45
44
 
@@ -48,13 +47,13 @@ class ProxyDetailsAdmin(AdminLTEModelView):
48
47
  def after_model_change(self, form, model, is_created):
49
48
  # if hconfig(ConfigEnum.parent_panel):
50
49
  # hiddify_api.sync_child_to_parent()
51
- hiddify.get_available_proxies.invalidate_all()
50
+ hutils.proxy.get_proxies.invalidate_all()
52
51
  pass
53
52
 
54
53
  def after_model_delete(self, model):
55
54
  # if hconfig(ConfigEnum.parent_panel):
56
55
  # hiddify_api.sync_child_to_parent()
57
- hiddify.get_available_proxies.invalidate_all()
56
+ hutils.proxy.get_proxies.invalidate_all()
58
57
  pass
59
58
 
60
59
  def is_accessible(self):
@@ -357,7 +357,7 @@ class AppAPI(MethodView):
357
357
  ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/hiddify-clash-desktop_{version}_amd64.AppImage'
358
358
  dto.install.append(self.__get_app_install_dto(AppInstallType.appimage, ins_url))
359
359
  case Platform.mac:
360
- ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyClashDesktop_{version}x64.dmg'
360
+ ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/HiddifyClashDesktop_{version}_x64.dmg'
361
361
  dto.install.append(self.__get_app_install_dto(AppInstallType.dmg, ins_url))
362
362
  else:
363
363
  match platform:
@@ -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()
@@ -108,7 +105,7 @@ class AllConfigsAPI(MethodView):
108
105
  )
109
106
  )
110
107
 
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
 
@@ -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):
@@ -21,6 +22,7 @@ def init_app(app: APIFlask):
21
22
  app.jinja_env.globals['static_url_for'] = hutils.flask.static_url_for
22
23
  app.jinja_env.globals['hurl_for'] = hutils.flask.hurl_for
23
24
  app.jinja_env.globals['_gettext'] = lambda x: print("==========", x)
25
+ app.jinja_env.globals['proxy_stats_url'] = hutils.flask.get_proxy_stats_url
24
26
 
25
27
  @app.after_request
26
28
  def apply_no_robot(response):
@@ -42,7 +44,7 @@ def init_app(app: APIFlask):
42
44
  has_update = False
43
45
  else:
44
46
  has_update = "dev" not in hiddifypanel.__version__ and f'{last_version}' != hiddifypanel.__version__
45
-
47
+
46
48
  if not request.accept_mimetypes.accept_html:
47
49
  if has_update:
48
50
  return jsonify({
@@ -91,11 +93,11 @@ def init_app(app: APIFlask):
91
93
  if 'proxy_path' not in values:
92
94
  if force_path := g.get('force_proxy_path'):
93
95
  values['proxy_path'] = force_path
94
- elif hutils.flask.is_admin_role(current_account.role):
96
+ elif hutils.flask.is_admin_role(current_account.role): # type: ignore
95
97
  values['proxy_path'] = hconfig(ConfigEnum.proxy_path_admin)
96
98
  elif hutils.flask.is_user_panel_call():
97
99
  values['proxy_path'] = hconfig(ConfigEnum.proxy_path_client)
98
- elif current_account and hutils.flask.is_admin_role(current_account.role):
100
+ elif current_account and hutils.flask.is_admin_role(current_account.role): # type: ignore
99
101
  values['proxy_path'] = hconfig(ConfigEnum.proxy_path_admin)
100
102
  else:
101
103
  values['proxy_path'] = g.proxy_path or "A"
@@ -68,52 +68,6 @@ def exec_command(cmd, cwd=None):
68
68
  print(e)
69
69
 
70
70
 
71
- @cache.cache(ttl=300)
72
- def get_available_proxies(child_id):
73
- proxies = Proxy.query.filter(Proxy.child_id == child_id).all()
74
- proxies = [c for c in proxies if 'restls' not in c.transport]
75
- # if not hconfig(ConfigEnum.tuic_enable, child_id):
76
- # proxies = [c for c in proxies if c.proto != ProxyProto.tuic]
77
- # if not hconfig(ConfigEnum.hysteria_enable, child_id):
78
- # proxies = [c for c in proxies if c.proto != ProxyProto.hysteria2]
79
- if not hconfig(ConfigEnum.shadowsocks2022_enable, child_id):
80
- proxies = [c for c in proxies if 'shadowsocks' != c.transport]
81
-
82
- if not hconfig(ConfigEnum.ssfaketls_enable, child_id):
83
- proxies = [c for c in proxies if 'faketls' != c.transport]
84
- if not hconfig(ConfigEnum.v2ray_enable, child_id):
85
- proxies = [c for c in proxies if 'v2ray' != c.proto]
86
- if not hconfig(ConfigEnum.shadowtls_enable, child_id):
87
- proxies = [c for c in proxies if c.transport != 'shadowtls']
88
- if not hconfig(ConfigEnum.ssr_enable, child_id):
89
- proxies = [c for c in proxies if 'ssr' != c.proto]
90
- if not hconfig(ConfigEnum.vmess_enable, child_id):
91
- proxies = [c for c in proxies if 'vmess' not in c.proto]
92
- if not hconfig(ConfigEnum.httpupgrade_enable, child_id):
93
- proxies = [c for c in proxies if ProxyTransport.httpupgrade not in c.transport]
94
- if not hconfig(ConfigEnum.ws_enable, child_id):
95
- proxies = [c for c in proxies if ProxyTransport.WS not in c.transport]
96
-
97
- if not hconfig(ConfigEnum.grpc_enable, child_id):
98
- proxies = [c for c in proxies if ProxyTransport.grpc not in c.transport]
99
- if not hconfig(ConfigEnum.kcp_enable, child_id):
100
- proxies = [c for c in proxies if 'kcp' not in c.l3]
101
-
102
- if not hconfig(ConfigEnum.http_proxy_enable, child_id):
103
- proxies = [c for c in proxies if 'http' != c.l3]
104
-
105
- if not Domain.query.filter(Domain.mode.in_([DomainType.cdn, DomainType.auto_cdn_ip])).first():
106
- proxies = [c for c in proxies if c.cdn != "CDN"]
107
-
108
- if not Domain.query.filter(Domain.mode.in_([DomainType.relay])).first():
109
- proxies = [c for c in proxies if c.cdn != ProxyCDN.relay]
110
-
111
- if not Domain.query.filter(Domain.mode.in_([DomainType.cdn, DomainType.auto_cdn_ip]), Domain.servernames != "", Domain.servernames != Domain.domain).first():
112
- proxies = [c for c in proxies if 'Fake' not in c.cdn]
113
- proxies = [c for c in proxies if not ('vless' == c.proto and ProxyTransport.tcp == c.transport and c.cdn == ProxyCDN.direct)]
114
- return proxies
115
-
116
-
117
71
  def quick_apply_users():
118
72
  if hconfig(ConfigEnum.is_parent):
119
73
  return
@@ -358,54 +312,10 @@ def generate_x25519_keys():
358
312
  return {'private_key': priv_str, 'public_key': pub_str}
359
313
 
360
314
 
361
- def get_hostkeys(dojson=False):
362
- key_files = glob.glob(current_app.config['HIDDIFY_CONFIG_PATH'] + "/other/ssh/host_key/*_key.pub")
363
- host_keys = []
364
- for file_name in key_files:
365
- with open(file_name, "r") as f:
366
- host_key = f.read().strip()
367
- host_key = host_key.split()
368
- if len(host_key) > 2:
369
- host_key = host_key[:2] # strip the hostname part
370
- host_key = " ".join(host_key)
371
- host_keys.append(host_key)
372
- if dojson:
373
- return json.dumps(host_keys)
374
- return host_keys
375
-
376
-
377
315
  def get_ssh_client_version(user):
378
316
  return 'SSH-2.0-OpenSSH_7.4p1'
379
317
 
380
318
 
381
- def get_ed25519_private_public_pair():
382
- from cryptography.hazmat.primitives.asymmetric import ed25519
383
- from cryptography.hazmat.primitives import serialization
384
- privkey = ed25519.Ed25519PrivateKey.generate()
385
- pubkey = privkey.public_key()
386
- priv_bytes = privkey.private_bytes(
387
- encoding=serialization.Encoding.PEM,
388
- format=serialization.PrivateFormat.OpenSSH,
389
- encryption_algorithm=serialization.NoEncryption(),
390
- )
391
- pub_bytes = pubkey.public_bytes(
392
- encoding=serialization.Encoding.OpenSSH,
393
- format=serialization.PublicFormat.OpenSSH,
394
- )
395
- return priv_bytes.decode(), pub_bytes.decode()
396
-
397
-
398
- def get_wg_private_public_psk_pair():
399
- try:
400
- private_key = subprocess.run(["wg", "genkey"], capture_output=True, text=True, check=True).stdout.strip()
401
- public_key = subprocess.run(["wg", "pubkey"], input=private_key, capture_output=True, text=True, check=True).stdout.strip()
402
- psk = subprocess.run(["wg", "genpsk"], capture_output=True, text=True, check=True).stdout.strip()
403
- return private_key, public_key, psk
404
- except subprocess.CalledProcessError as e:
405
- print(f"Error: {e}")
406
- return None, None, None
407
-
408
-
409
319
  def get_account_panel_link(account: BaseAccount, host: str, is_https: bool = True, prefere_path_only: bool = False, child_id=None):
410
320
  if child_id is None:
411
321
  child_id = Child.current.id