hiddifypanel 10.30.8.dev1__py3-none-any.whl → 10.30.9.dev1__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 (54) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/cache.py +13 -3
  4. hiddifypanel/hutils/network/net.py +41 -10
  5. hiddifypanel/hutils/proxy/shared.py +1 -1
  6. hiddifypanel/hutils/proxy/xrayjson.py +6 -6
  7. hiddifypanel/models/config_enum.py +2 -2
  8. hiddifypanel/models/domain.py +1 -1
  9. hiddifypanel/models/user.py +3 -2
  10. hiddifypanel/panel/admin/Actions.py +2 -2
  11. hiddifypanel/panel/admin/AdminstratorAdmin.py +1 -1
  12. hiddifypanel/panel/admin/Dashboard.py +3 -4
  13. hiddifypanel/panel/admin/DomainAdmin.py +48 -35
  14. hiddifypanel/panel/admin/ProxyAdmin.py +1 -1
  15. hiddifypanel/panel/admin/QuickSetup.py +144 -52
  16. hiddifypanel/panel/admin/SettingAdmin.py +23 -18
  17. hiddifypanel/panel/admin/UserAdmin.py +7 -4
  18. hiddifypanel/panel/admin/templates/model/domain_list.html +13 -12
  19. hiddifypanel/panel/admin/templates/model/user_list.html +8 -5
  20. hiddifypanel/panel/admin/templates/quick_setup.html +20 -14
  21. hiddifypanel/panel/admin/templates/result.html +3 -2
  22. hiddifypanel/panel/commercial/ParentDomainAdmin.py +1 -1
  23. hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +3 -0
  24. hiddifypanel/panel/commercial/telegrambot/Usage.py +1 -4
  25. hiddifypanel/panel/commercial/telegrambot/admin.py +3 -3
  26. hiddifypanel/panel/commercial/telegrambot/information.py +1 -3
  27. hiddifypanel/panel/common_bp/login.py +2 -2
  28. hiddifypanel/panel/init_db.py +6 -6
  29. hiddifypanel/panel/user/templates/home/home.html +30 -14
  30. hiddifypanel/panel/user/templates/home/index_old.html +2 -2
  31. hiddifypanel/panel/user/templates/home/multi.html +2 -2
  32. hiddifypanel/panel/user/templates/redirect_to_new_format.html +1 -1
  33. hiddifypanel/templates/500.html +1 -1
  34. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  35. hiddifypanel/translations/en/LC_MESSAGES/messages.po +128 -133
  36. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  37. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +130 -136
  38. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  39. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +115 -129
  40. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  41. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +120 -134
  42. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  43. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +104 -116
  44. hiddifypanel/translations.i18n/en.json +52 -73
  45. hiddifypanel/translations.i18n/fa.json +133 -75
  46. hiddifypanel/translations.i18n/pt.json +337 -70
  47. hiddifypanel/translations.i18n/ru.json +190 -75
  48. hiddifypanel/translations.i18n/zh.json +318 -70
  49. {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/METADATA +1 -1
  50. {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/RECORD +54 -54
  51. {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/LICENSE.md +0 -0
  52. {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/WHEEL +0 -0
  53. {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/entry_points.txt +0 -0
  54. {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION CHANGED
@@ -1 +1 @@
1
- 10.30.8.dev1
1
+ 10.30.9.dev1
hiddifypanel/VERSION.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__='10.30.8.dev1'
1
+ __version__='10.30.9.dev1'
2
2
  from datetime import datetime
3
- __release_date__= datetime.strptime('2024-07-10','%Y-%m-%d')
3
+ __release_date__= datetime.strptime('2024-07-13','%Y-%m-%d')
hiddifypanel/cache.py CHANGED
@@ -1,4 +1,4 @@
1
- from redis_cache import RedisCache, chunks
1
+ from redis_cache import RedisCache, chunks, compact_dump
2
2
  import redis
3
3
  from pickle import dumps, loads
4
4
  from loguru import logger
@@ -7,11 +7,21 @@ redis_client = redis.from_url('unix:///opt/hiddify-manager/other/redis/run.sock?
7
7
 
8
8
 
9
9
  class CustomRedisCache(RedisCache):
10
+ def __init__(self, redis_client, prefix="rc", serializer=compact_dump, deserializer=loads, key_serializer=None, support_cluster=True, exception_handler=None):
11
+ super().__init__(redis_client, prefix, serializer, deserializer, key_serializer, support_cluster, exception_handler)
12
+ self.cached_functions = set()
13
+
14
+ def cache(self, ttl=0, limit=0, namespace=None, exception_handler=None):
15
+ res = super().cache(ttl, limit, namespace, exception_handler)
16
+ self.cached_functions.add(res)
17
+ return res
10
18
 
11
19
  def invalidate_all_cached_functions(self):
12
20
  try:
21
+ for f in self.cached_functions:
22
+ f.invalidate_all()
13
23
  logger.trace("Invalidating all cached functions")
14
- chunks_gen = chunks(f'{self.prefix}*', 500)
24
+ chunks_gen = chunks(f'{self.prefix}*', 5000)
15
25
  for keys in chunks_gen:
16
26
  self.client.delete(*keys)
17
27
  logger.trace("Successfully invalidated all cached functions")
@@ -22,4 +32,4 @@ class CustomRedisCache(RedisCache):
22
32
  return False
23
33
 
24
34
 
25
- cache = CustomRedisCache(redis_client=redis_client, prefix="h", serializer=dumps, deserializer=loads)
35
+ cache = CustomRedisCache(redis_client=redis_client, prefix="h", serializer=dumps, deserializer=loads)
@@ -1,4 +1,4 @@
1
- from typing import List, Literal, Union
1
+ from typing import List, Literal, Set, Union
2
2
  from urllib.parse import urlparse
3
3
  import urllib.request
4
4
  import ipaddress
@@ -33,17 +33,43 @@ def get_domain_ip(domain: str, retry: int = 3, version: Literal[4, 6] | None = N
33
33
 
34
34
  if not res and version != 4:
35
35
  try:
36
- res = f"[{socket.getaddrinfo(domain, None, socket.AF_INET6)[0][4][0]}]"
37
-
38
- res = res[1:-1]
36
+ res = f"{socket.getaddrinfo(domain, None, socket.AF_INET6)[0][4][0]}"
39
37
 
40
38
  except BaseException:
41
39
  pass
42
40
 
43
- if retry <= 0 or not res:
41
+ if retry <= 0:
44
42
  return None
43
+ if not res:
44
+ return get_domain_ip(domain, retry=retry - 1, version=version)
45
+
46
+ return ipaddress.ip_address(res)
47
+
48
+
49
+ def get_domain_ips(domain: str, retry: int = 3) -> Set[Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]:
50
+ res = set()
51
+ if retry < 0:
52
+ return res
53
+ try:
54
+ _, _, ips = socket.gethostbyname_ex(domain)
55
+ for ip in ips:
56
+ res.add(ipaddress.ip_address(ip))
57
+ except Exception:
58
+ pass
59
+
60
+ try:
61
+ for ip in socket.getaddrinfo(domain, None, socket.AF_INET):
62
+ res.add(ipaddress.ip_address(ip[4][0]))
63
+ except BaseException:
64
+ pass
65
+
66
+ try:
67
+ for ip in socket.getaddrinfo(domain, None, socket.AF_INET6):
68
+ res.add(ipaddress.ip_address(ip[4][0]))
69
+ except BaseException:
70
+ pass
45
71
 
46
- return ipaddress.ip_address(res) or get_domain_ip(domain, retry=retry - 1) if res else None
72
+ return res or get_domain_ips(domain, retry=retry - 1)
47
73
 
48
74
 
49
75
  def get_socket_public_ip(version: Literal[4, 6]) -> Union[ipaddress.IPv4Address, ipaddress.IPv6Address, None]:
@@ -85,8 +111,11 @@ def get_interface_public_ip(version: Literal[4, 6]) -> List[Union[ipaddress.IPv4
85
111
 
86
112
 
87
113
  @cache.cache(ttl=600)
88
- def get_ips(version: Literal[4, 6]) -> List[Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]:
114
+ def get_ips(version: Literal[4, 6] | None = None) -> List[Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]:
115
+ if not version:
116
+ return [*get_ips(4), *get_ips(6)]
89
117
  addrs = []
118
+
90
119
  i_ips = get_interface_public_ip(version)
91
120
  if i_ips:
92
121
  addrs = i_ips
@@ -136,6 +165,7 @@ def get_ip(version: Literal[4, 6], retry: int = 5) -> ipaddress.IPv4Address | ip
136
165
  ip = get_ip(version, retry=retry - 1)
137
166
  return ip
138
167
 
168
+
139
169
  def get_random_domains(count: int = 1, retry: int = 3) -> List[str]:
140
170
  try:
141
171
  irurl = "https://api.ooni.io/api/v1/measurements?probe_cc=IR&test_name=web_connectivity&anomaly=false&confirmed=false&failure=false&order_by=test_start_time&limit=1000"
@@ -310,7 +340,7 @@ def is_in_same_asn(domain_or_ip: str, domain_or_ip_target: str) -> bool:
310
340
  print(f"An error occurred: {e}")
311
341
  return False
312
342
 
313
- # 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.") +
343
+ # hutils.flask.flash(_("domain.reality.asn_issue") +
314
344
  # f"<br> Server ASN={asn_ipv4.get('autonomous_system_organization','unknown')}<br>{domain}_ASN={asn_dip.get('autonomous_system_organization','unknown')}", "warning")
315
345
 
316
346
 
@@ -342,8 +372,9 @@ def is_ip(input: str):
342
372
  except:
343
373
  return False
344
374
 
345
- def resolve_domain_with_api(domain:str) -> str:
375
+
376
+ def resolve_domain_with_api(domain: str) -> str:
346
377
  if not domain:
347
378
  return ''
348
379
  endpoint = f'http://ip-api.com/json/{domain}?fields=query'
349
- return str(requests.get(endpoint).json().get('query'))
380
+ return str(requests.get(endpoint).json().get('query'))
@@ -130,7 +130,7 @@ def get_proxies(child_id: int = 0, only_enabled=False) -> list['Proxy']:
130
130
  if not hconfig(ConfigEnum.vmess_enable, child_id):
131
131
  proxies = [c for c in proxies if 'vmess' not in c.proto]
132
132
  if not hconfig(ConfigEnum.vless_enable, child_id):
133
- proxies = [c for c in proxies if 'vless' not in c.proto]
133
+ proxies = [c for c in proxies if 'vless' not in c.proto or 'reality' in c.l3]
134
134
  if not hconfig(ConfigEnum.trojan_enable, child_id):
135
135
  proxies = [c for c in proxies if 'trojan' not in c.proto]
136
136
  if not hconfig(ConfigEnum.httpupgrade_enable, child_id):
@@ -223,8 +223,10 @@ def add_stream_settings(base: dict, proxy: dict):
223
223
  # THE CURRENT CODE WORKS BUT THE CORRECT CONDITINO SHOULD BE THIS:
224
224
  # ss['security'] == 'tls' or 'xtls' -----> ss['security'] in ['tls','xtls']
225
225
  # TODO: FIX THE CONDITION AND TEST CONFIGS ON THE CLIENT SIDE
226
-
227
- if ss['security'] == 'tls' or 'xtls' and proxy['proto'] != ProxyProto.ss:
226
+ if ss['security'] == 'reality':
227
+ ss['network'] = proxy['transport']
228
+ add_reality_stream(ss, proxy)
229
+ elif ss['security'] == 'tls' or 'xtls' and proxy['proto'] != ProxyProto.ss:
228
230
  ss['tlsSettings'] = {
229
231
  'serverName': proxy['sni'],
230
232
  'allowInsecure': proxy['allow_insecure'],
@@ -239,9 +241,7 @@ def add_stream_settings(base: dict, proxy: dict):
239
241
  # 'cipherSuites': '', # Go lang sets
240
242
  # 'rejectUnknownSni': '', # default is false
241
243
  }
242
- if ss['security'] == 'reality':
243
- ss['network'] = proxy['transport']
244
- add_reality_stream(ss, proxy)
244
+
245
245
  if proxy['l3'] == ProxyL3.kcp:
246
246
  ss['network'] = 'kcp'
247
247
  add_kcp_stream(ss, proxy)
@@ -249,7 +249,7 @@ def add_stream_settings(base: dict, proxy: dict):
249
249
  if proxy['l3'] == ProxyL3.h3_quic:
250
250
  add_quic_stream(ss, proxy)
251
251
 
252
- if proxy['transport'] == 'tcp' or ss['security'] == 'reality' or (ss['security'] == 'none' and proxy['transport'] not in [ProxyTransport.httpupgrade, ProxyTransport.WS] and proxy['proto'] != ProxyProto.ss):
252
+ if (proxy['transport'] == 'tcp' and ss['security'] != 'reality') or (ss['security'] == 'none' and proxy['transport'] not in [ProxyTransport.httpupgrade, ProxyTransport.WS] and proxy['proto'] != ProxyProto.ss):
253
253
  ss['network'] = proxy['transport']
254
254
  add_tcp_stream(ss, proxy)
255
255
  if proxy['transport'] == ProxyTransport.h2 and ss['security'] == 'none' and ss['security'] != 'reality':
@@ -243,8 +243,8 @@ class ConfigEnum(metaclass=FastEnum):
243
243
  trojan_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
244
244
  reality_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
245
245
  tcp_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
246
- quic_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
247
- xtls_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
246
+ quic_enable = _BoolConfigDscr(ConfigCategory.hidden, ApplyMode.apply)
247
+ xtls_enable = _BoolConfigDscr(ConfigCategory.hidden, ApplyMode.apply)
248
248
  h2_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
249
249
 
250
250
  db_version = _StrConfigDscr(ConfigCategory.hidden, ApplyMode.apply)
@@ -38,7 +38,7 @@ ShowDomain = db.Table('show_domain',
38
38
  class Domain(db.Model, SerializerMixin):
39
39
  id = db.Column(db.Integer, primary_key=True, autoincrement=True)
40
40
  child_id = db.Column(db.Integer, db.ForeignKey('child.id'), default=0)
41
- domain = db.Column(db.String(200), nullable=False, unique=False)
41
+ domain = db.Column(db.String(200), nullable=True, unique=False)
42
42
  alias = db.Column(db.String(200))
43
43
  sub_link_only = db.Column(db.Boolean, nullable=False, default=False)
44
44
  mode = db.Column(db.Enum(DomainType), nullable=False, default=DomainType.direct)
@@ -290,7 +290,7 @@ class User(BaseAccount, SerializerMixin):
290
290
  return schema.dump(User())
291
291
 
292
292
  def to_schema(self):
293
- user_dict = self.to_dict()
293
+ user_dict = self.to_dict(dump_id=True)
294
294
  from hiddifypanel.panel.commercial.restapi.v2.admin.user_api import UserSchema
295
295
  return UserSchema().load(user_dict)
296
296
 
@@ -317,7 +317,8 @@ class User(BaseAccount, SerializerMixin):
317
317
  'wg_pk': self.wg_pk,
318
318
  'wg_pub': self.wg_pub,
319
319
  'wg_psk': self.wg_psk,
320
- 'is_active': self.is_active
320
+ 'is_active': self.is_active,
321
+ 'enable': self.enable
321
322
  }
322
323
 
323
324
  # @staticmethod
@@ -65,7 +65,7 @@ class Actions(FlaskView):
65
65
  domain_changed = request.args.get("domain_changed", str(domain_changed)).lower() == "true"
66
66
  complete_install = request.args.get("complete_install", str(complete_install)).lower() == "true"
67
67
  if domain_changed:
68
- hutils.flask.flash((_('Your domains changed. Please do not forget to copy admin links, otherwise you can not access to the panel anymore.')), 'info')
68
+ hutils.flask.flash((_('domain.changed_in_domain_warning')), 'info')
69
69
  # hutils.flask.flash(f'complete_install={complete_install} domain_changed={domain_changed} ', 'info')
70
70
  # return render_template("result.html")
71
71
  # hiddify.add_temporary_access()
@@ -87,7 +87,7 @@ class Actions(FlaskView):
87
87
 
88
88
  resp = render_template("result.html",
89
89
  out_type="info",
90
- out_msg=_("Success! Please wait around 4 minutes to make sure everything is updated. During this time, please save your proxy links which are:") +
90
+ out_msg=_("admin.waiting_for_update") +
91
91
  admin_links,
92
92
  log_file_url=get_log_api_url(),
93
93
  log_file="0-install.log",
@@ -72,7 +72,7 @@ class AdminstratorAdmin(AdminLTEModelView):
72
72
 
73
73
  column_descriptions = dict(
74
74
  comment=_("Add some text that is only visible to super_admin."),
75
- mode=_("Define the admin mode. "),
75
+ mode=_("admin.define_mode"),
76
76
  )
77
77
  # create_modal = True
78
78
  can_export = False
@@ -22,7 +22,7 @@ class Dashboard(FlaskView):
22
22
  return redirect(hurl_for("admin.QuickSetup:index"))
23
23
 
24
24
  if hutils.utils.is_panel_outdated():
25
- hutils.flask.flash(_('This version of hiddify panel is outdated. Please update it from admin area.'), "danger") # type: ignore
25
+ hutils.flask.flash(_('outdated_panel'), "danger") # type: ignore
26
26
 
27
27
  childs = None
28
28
  admin_id = request.args.get("admin_id") or g.account.id
@@ -48,8 +48,7 @@ class Dashboard(FlaskView):
48
48
 
49
49
  if def_user and sslip_domains:
50
50
  quick_setup = hurl_for("admin.QuickSetup:index")
51
- 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.',
52
- quick_setup=quick_setup)), 'warning') # type: ignore
51
+ hutils.flask.flash((_('admin.incomplete_setup_warning', quick_setup=quick_setup)), 'warning') # type: ignore
53
52
  if hutils.node.is_parent():
54
53
  hutils.flask.flash(
55
54
  _("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
@@ -61,7 +60,7 @@ class Dashboard(FlaskView):
61
60
  _("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
62
61
  elif def_user:
63
62
  d = domains[0]
64
- hutils.flask.flash((_('It seems that you have not created any users yet. Default user link: %(default_link)s',
63
+ hutils.flask.flash((_("admin.no_user_warning",
65
64
  default_link=hiddify.get_html_user_link(def_user, d))), 'secondary') # type: ignore
66
65
  if hutils.network.is_ssh_password_authentication_enabled():
67
66
  hutils.flask.flash(_('serverssh.password-login.warning'), "warning") # type: ignore
@@ -46,11 +46,11 @@ class DomainAdmin(AdminLTEModelView):
46
46
  mode=_("Direct mode means you want to use your server directly (for usual use), CDN means that you use your server on behind of a CDN provider."),
47
47
  cdn_ip=_("config.cdn_forced_host.description"),
48
48
  show_domains=_(
49
- 'You can select the configs with which domains show be shown in the user area. If you select all, automatically, all the new domains will be added for each users.'),
49
+ 'domain.show_domains_description'),
50
50
  alias=_('The name shown in the configs for this domain.'),
51
51
  servernames=_('config.reality_server_names.description'),
52
52
  sub_link_only=_('This can be used for giving your users a permanent non blockable links.'),
53
- grpc=_('gRPC is a H2 based protocol. Maybe it is faster for you!'))
53
+ grpc=_('grpc-proxy.description'))
54
54
 
55
55
  # create_modal = True
56
56
  can_export = False
@@ -65,7 +65,7 @@ class DomainAdmin(AdminLTEModelView):
65
65
  'domain': {
66
66
  'validators': [
67
67
  Regexp(
68
- r'^(\*\.)?([A-Za-z0-9\-\.]+\.[a-zA-Z]{2,})$',
68
+ r'^(\*\.)?([A-Za-z0-9\-\.]+\.[a-zA-Z]{2,})$|^$',
69
69
  message=__("Should be a valid domain"))]},
70
70
  "cdn_ip": {
71
71
  'validators': [
@@ -109,21 +109,24 @@ class DomainAdmin(AdminLTEModelView):
109
109
  f'</a><a href="{admin_link}" class="btn btn-xs btn-info ltr" target="_blank">{model.domain}</a></div>')
110
110
 
111
111
  def _domain_ip(view, context, model, name):
112
- dip = hutils.network.get_domain_ip(model.domain)
112
+ dips = hutils.network.get_domain_ips(model.domain)
113
113
  # The get_domain_ip function uses the socket library, which relies on the system DNS resolver. So it may sometimes use cached data, which is not desirable
114
- if not dip:
115
- dip = hutils.network.resolve_domain_with_api(model.domain)
116
- myip = hutils.network.get_ip(4)
117
- if myip == dip and model.mode in [DomainType.direct, DomainType.sub_link_only]:
118
- badge_type = ''
119
- elif dip and model.mode != DomainType.direct and myip != dip:
120
- badge_type = 'warning'
121
- else:
122
- badge_type = 'danger'
123
- res = f'<span class="badge badge-{badge_type}">{dip}</span>'
124
- if model.sub_link_only:
125
- res += f'<span class="badge badge-success">{_("SubLink")}</span>'
126
- return Markup(res)
114
+ # if not dips:
115
+ # dip = hutils.network.resolve_domain_with_api(model.domain)
116
+ myips = set(hutils.network.get_ips())
117
+ all_res = ""
118
+ for dip in dips:
119
+ if dip in myips and model.mode in [DomainType.direct, DomainType.sub_link_only]:
120
+ badge_type = ''
121
+ elif dip and dip not in myips and model.mode != DomainType.direct:
122
+ badge_type = 'warning'
123
+ else:
124
+ badge_type = 'danger'
125
+ res = f'<span class="badge badge-{badge_type}">{dip}</span>'
126
+ if model.sub_link_only:
127
+ res += f'<span class="badge badge-success">{_("SubLink")}</span>'
128
+ all_res += res
129
+ return Markup(all_res)
127
130
 
128
131
  def _show_domains_formater(view, context, model, name):
129
132
  if not len(model.show_domains):
@@ -149,8 +152,9 @@ class DomainAdmin(AdminLTEModelView):
149
152
 
150
153
  # TODO: refactor this function
151
154
  def on_model_change(self, form, model, is_created):
152
- model.domain = model.domain.lower()
153
-
155
+ model.domain = (model.domain or '').lower()
156
+ if model.domain == '' and model.mode != DomainType.fake:
157
+ raise ValidationError(_("domain.empty.allowed_for_fake_only"))
154
158
  configs = get_hconfigs()
155
159
  for c in configs:
156
160
  if "domain" in c and c not in [ConfigEnum.decoy_domain, ConfigEnum.reality_fallback_domain] and c.category != 'hidden':
@@ -174,7 +178,7 @@ class DomainAdmin(AdminLTEModelView):
174
178
  if "*" in model.domain and model.mode not in [DomainType.cdn, DomainType.auto_cdn_ip]:
175
179
  raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
176
180
 
177
- skip_check = "*" in model.domain
181
+ skip_check = "*" in model.domain or model.domain == ""
178
182
  if hconfig(ConfigEnum.cloudflare) and model.mode not in [DomainType.fake, DomainType.relay, DomainType.reality]:
179
183
  try:
180
184
  proxied = model.mode in [DomainType.cdn, DomainType.auto_cdn_ip]
@@ -190,28 +194,30 @@ class DomainAdmin(AdminLTEModelView):
190
194
  # hutils.flask.flash(__("Using alias with special charachters may cause problem in some clients like FairVPN."), 'warning')
191
195
  # raise ValidationError(_("You have to add your cloudflare api key to use this feature: "))
192
196
 
193
- dip = hutils.network.get_domain_ip(model.domain)
197
+ dips = hutils.network.get_domain_ips(model.domain)
194
198
  if model.sub_link_only:
195
- if dip is None:
199
+ if not dips:
196
200
  raise ValidationError(_("Domain can not be resolved! there is a problem in your domain")) # type: ignore
197
201
  elif not skip_check:
198
- if dip is None:
202
+ if not dips:
199
203
  raise ValidationError(_("Domain can not be resolved! there is a problem in your domain")) # type: ignore
200
204
 
201
205
  domain_ip_is_same_as_panel = False
202
- domain_ip_is_same_as_panel |= dip in ipv4_list
203
- for ipv6 in ipv6_list:
204
- domain_ip_is_same_as_panel |= ipaddress.ip_address(dip) == ipaddress.ip_address(ipv6)
206
+ server_ips = [*ipv4_list, *ipv6_list]
207
+ for mip in server_ips:
208
+ domain_ip_is_same_as_panel |= mip in dips
209
+ server_ips_str = ', '.join(list(map(str, server_ips)))
210
+ dips_str = ', '.join(list(map(str, dips)))
205
211
 
206
212
  if model.mode == DomainType.direct and not domain_ip_is_same_as_panel:
207
213
  # 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')
208
214
  raise ValidationError(
209
- __("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
215
+ __("Domain IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode", server_ip=server_ips_str, domain_ip=dips_str)) # type: ignore
210
216
 
211
217
  if domain_ip_is_same_as_panel and model.mode in [DomainType.cdn, DomainType.relay, DomainType.fake, DomainType.auto_cdn_ip]:
212
218
  # # hutils.flask.flash(__(f"In CDN mode, Domain IP={dip} should be different to your ip={', '.join(list(map(str, ipv4_list)))}"), 'warning')
213
219
  raise ValidationError(__("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s",
214
- server_ip=', '.join(list(map(str, ipv4_list))), domain_ip=dip)) # type: ignore
220
+ server_ip=server_ips_str, domain_ip=dips_str)) # type: ignore
215
221
 
216
222
  # if model.mode in [DomainType.ss_faketls, DomainType.telegram_faketls]:
217
223
  # if len(Domain.query.filter(Domain.mode==model.mode and Domain.id!=model.id).all())>0:
@@ -219,10 +225,11 @@ class DomainAdmin(AdminLTEModelView):
219
225
 
220
226
  model.domain = model.domain.lower()
221
227
  if model.mode == DomainType.direct and model.cdn_ip:
228
+ model.cdn_ip = ""
222
229
  raise ValidationError(f"Specifying CDN IP is only valid for CDN mode")
223
230
 
224
231
  if model.mode == DomainType.fake and not model.cdn_ip:
225
- model.cdn_ip = str(ipv4_list[0])
232
+ model.cdn_ip = str(server_ips[0])
226
233
 
227
234
  # if model.mode==DomainType.fake and model.cdn_ip!=myip:
228
235
  # raise ValidationError(f"Specifying CDN IP is only valid for CDN mode")
@@ -230,8 +237,14 @@ class DomainAdmin(AdminLTEModelView):
230
237
  # # Update the many-to-many relationship
231
238
  if len(model.show_domains) == Domain.query.count():
232
239
  model.show_domains = []
233
-
234
- if model.mode == DomainType.reality:
240
+ if model.mode == DomainType.old_xtls_direct:
241
+ if not hconfig(ConfigEnum.xtls_enable):
242
+ set_hconfig(ConfigEnum.xtls_enable, True)
243
+ hutils.proxy.get_proxies().invalidate_all()
244
+ elif model.mode == DomainType.reality:
245
+ if not hconfig(ConfigEnum.reality_enable):
246
+ set_hconfig(ConfigEnum.reality_enable, True)
247
+ hutils.proxy.get_proxies().invalidate_all()
235
248
  model.servernames = (model.servernames or model.domain).lower()
236
249
  for v in set([model.domain, model.servernames]):
237
250
  for d in v.split(","):
@@ -240,10 +253,10 @@ class DomainAdmin(AdminLTEModelView):
240
253
  if not hutils.network.is_domain_reality_friendly(d): # the minimum requirement for the REALITY protocol is to have tls1.3 and h2
241
254
  raise ValidationError(_("Domain is not REALITY friendly!") + f' {d}')
242
255
 
243
- if not hutils.network.is_in_same_asn(d, ipv4_list[0]):
244
- server_asn = hutils.network.get_ip_asn(ipv4_list[0])
245
- domain_asn = hutils.network.get_ip_asn(dip) # type: ignore
246
- 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.") + \
256
+ if not hutils.network.is_in_same_asn(d, server_ips[0]):
257
+ server_asn = hutils.network.get_ip_asn(server_ips[0])
258
+ domain_asn = hutils.network.get_ip_asn(dips[0]) # type: ignore
259
+ msg = _("domain.reality.asn_issue") + \
247
260
  (f"<br> Server ASN={server_asn}<br>{d}_ASN={domain_asn}" if server_asn or domain_asn else "")
248
261
  hutils.flask.flash(msg, 'warning')
249
262
 
@@ -69,7 +69,7 @@ class ProxyAdmin(FlaskView):
69
69
 
70
70
 
71
71
  def get_global_config_form(empty=False):
72
- boolconfigs = BoolConfig.query.all()
72
+ boolconfigs = BoolConfig.query.filter(BoolConfig.child_id == Child.current().id).all()
73
73
 
74
74
  class DynamicForm(FlaskForm):
75
75
  pass