hiddifypanel 10.80.0.dev13__py3-none-any.whl → 10.80.0.dev14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hiddifypanel/VERSION CHANGED
@@ -1 +1 @@
1
- 10.80.0.dev13
1
+ 10.80.0.dev14
hiddifypanel/VERSION.py CHANGED
@@ -2,5 +2,5 @@ import importlib.metadata
2
2
  from datetime import datetime
3
3
 
4
4
  __version__ = importlib.metadata.version(__package__ or __name__)
5
- __release_time__= datetime.strptime('2024-11-16T22:55:11','%Y-%m-%dT%H:%M:%S')
5
+ __release_time__= datetime.strptime('2024-11-18T03:24:43','%Y-%m-%dT%H:%M:%S')
6
6
  is_released_version=True
@@ -33,7 +33,7 @@ def to_clash(proxy, meta_or_normal):
33
33
 
34
34
  if proxy['l3'] in ["kcp", ProxyL3.h3_quic]:
35
35
  return {'name': name, 'msg': f"clash does not support {proxy['l3']}", 'type': 'debug'}
36
- if proxy['transport'] in [ProxyTransport.splithttp, ProxyTransport.httpupgrade]:
36
+ if proxy['transport'] in [ProxyTransport.xhttp, ProxyTransport.httpupgrade]:
37
37
  return {'name': name, 'msg': f"clash does not support {proxy['transport']}", 'type': 'debug'}
38
38
  # if proxy['proto'] in [Proxy.shado]:
39
39
 
@@ -44,7 +44,7 @@ def to_clash(proxy, meta_or_normal):
44
44
  return {'name': name, 'msg': f"clash does not support {proxy['proto']}", 'type': 'debug'}
45
45
  if proxy['proto'] in ["vless", 'tuic', 'hysteria2']:
46
46
  return {'name': name, 'msg': f"{proxy['proto']} not supported in clash", 'type': 'debug'}
47
- if proxy['transport'] in ["shadowtls", "splithttp"]:
47
+ if proxy['transport'] in ["shadowtls", "xhttp"]:
48
48
  return {'name': name, 'msg': f"{proxy['transport']} not supported in clash", 'type': 'debug'}
49
49
  if proxy['l3'] == ProxyL3.tls_h2 and proxy['proto'] in [ProxyProto.vmess, ProxyProto.vless] and proxy['dbe'].cdn == ProxyCDN.direct:
50
50
  return {'name': name, 'msg': "bug tls_h2 vmess and vless in clash meta", 'type': 'warning'}
@@ -134,8 +134,8 @@ def get_proxies(child_id: int = 0, only_enabled=False) -> list['Proxy']:
134
134
  proxies = [c for c in proxies if 'trojan' not in c.proto]
135
135
  if not hconfig(ConfigEnum.httpupgrade_enable, child_id):
136
136
  proxies = [c for c in proxies if ProxyTransport.httpupgrade not in c.transport]
137
- if not hconfig(ConfigEnum.splithttp_enable, child_id):
138
- proxies = [c for c in proxies if ProxyTransport.splithttp not in c.transport]
137
+ if not hconfig(ConfigEnum.xhttp_enable, child_id):
138
+ proxies = [c for c in proxies if ProxyTransport.xhttp not in c.transport]
139
139
  if not hconfig(ConfigEnum.ws_enable, child_id):
140
140
  proxies = [c for c in proxies if ProxyTransport.WS not in c.transport]
141
141
  # if not hconfig(ConfigEnum.xtls_enable, child_id):
@@ -189,7 +189,7 @@ def get_valid_proxies(domains: list[Domain]) -> list[dict]:
189
189
  noDomainProxies = False
190
190
  if proxy.proto in [ProxyProto.ssh, ProxyProto.wireguard]:
191
191
  noDomainProxies = True
192
- if proxy.proto in [ProxyProto.ss] and proxy.transport not in [ProxyTransport.grpc, ProxyTransport.h2, ProxyTransport.WS, ProxyTransport.httpupgrade, ProxyTransport.splithttp]:
192
+ if proxy.proto in [ProxyProto.ss] and proxy.transport not in [ProxyTransport.grpc, ProxyTransport.h2, ProxyTransport.WS, ProxyTransport.httpupgrade, ProxyTransport.xhttp]:
193
193
  noDomainProxies = True
194
194
  options = []
195
195
  key = f'{proxy.proto}{proxy.transport}{proxy.cdn}{proxy.l3}'
@@ -413,9 +413,9 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
413
413
  base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_httpupgrade]}'
414
414
  base["host"] = domain
415
415
  return base
416
- if proxy.transport in [ProxyTransport.splithttp]:
417
- base['transport'] = 'splithttp'
418
- base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_splithttp]}'
416
+ if proxy.transport in [ProxyTransport.xhttp]:
417
+ base['transport'] = 'xhttp'
418
+ base['path'] = f'/{path[base["proto"]]}{hconfigs[ConfigEnum.path_xhttp]}'
419
419
  # if 0 and 'h2' in base['alpn'] or 'h3' in base['alpn']:
420
420
  # base['path'] += "2"
421
421
  # else:
@@ -47,7 +47,7 @@ def configs_as_json(domains: list[Domain], **kwargs) -> str:
47
47
  def is_xray_proxy(proxy: dict):
48
48
  if g.user_agent.get('is_hiddify_prefere_xray'):
49
49
  return True
50
- if proxy['transport'] == ProxyTransport.splithttp:
50
+ if proxy['transport'] == ProxyTransport.xhttp:
51
51
  return True
52
52
  return False
53
53
 
@@ -126,10 +126,10 @@ def to_link(proxy: dict) -> str | dict:
126
126
  baseurl += "&encryption=none"
127
127
  if proxy.get('fingerprint', 'none') != 'none':
128
128
  baseurl += "&fp=" + proxy['fingerprint']
129
- if proxy.get('transport') in {ProxyTransport.splithttp}:
129
+ if proxy.get('transport') in {ProxyTransport.xhttp}:
130
130
  baseurl += "&core=xray"
131
131
  if proxy['l3'] != 'quic':
132
- if proxy.get('l3') != ProxyL3.reality and (proxy.get('transport') in {ProxyTransport.tcp, ProxyTransport.httpupgrade, ProxyTransport.splithttp}) and proxy['proto'] in [ProxyProto.vless, ProxyProto.trojan]:
132
+ if proxy.get('l3') != ProxyL3.reality and (proxy.get('transport') in {ProxyTransport.tcp, ProxyTransport.httpupgrade, ProxyTransport.xhttp}) and proxy['proto'] in [ProxyProto.vless, ProxyProto.trojan]:
133
133
  baseurl += '&headerType=http'
134
134
  else:
135
135
  baseurl += '&headerType=None'
@@ -262,9 +262,9 @@ def add_stream_settings(base: dict, proxy: dict):
262
262
  if proxy['transport'] == ProxyTransport.httpupgrade:
263
263
  ss['network'] = proxy['transport']
264
264
  add_httpupgrade_stream(ss, proxy)
265
- if proxy['transport'] == ProxyTransport.splithttp:
265
+ if proxy['transport'] == ProxyTransport.xhttp:
266
266
  ss['network'] = proxy['transport']
267
- add_splithttp_stream(ss, proxy)
267
+ add_xhttp_stream(ss, proxy)
268
268
  if proxy['transport'] == 'ws':
269
269
  ss['network'] = proxy['transport']
270
270
  add_ws_stream(ss, proxy)
@@ -338,8 +338,8 @@ def add_httpupgrade_stream(ss: dict, proxy: dict):
338
338
  }
339
339
 
340
340
 
341
- def add_splithttp_stream(ss: dict, proxy: dict):
342
- ss['splithttpSettings'] = {
341
+ def add_xhttp_stream(ss: dict, proxy: dict):
342
+ ss['xhttpSettings'] = {
343
343
  'path': proxy['path'],
344
344
  'host': proxy['host'],
345
345
  "headers": {
@@ -239,7 +239,7 @@ class ConfigEnum(metaclass=FastEnum):
239
239
  ws_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply_config)
240
240
  grpc_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply_config)
241
241
  httpupgrade_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply_config)
242
- splithttp_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply_config)
242
+ xhttp_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply_config)
243
243
 
244
244
  vless_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply_config)
245
245
  trojan_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply_config)
@@ -262,7 +262,7 @@ class ConfigEnum(metaclass=FastEnum):
262
262
  path_v2ray = _StrConfigDscr(ConfigCategory.hidden, ApplyMode.apply_config, hide_in_virtual_child=True) # deprecated
263
263
  path_ss = _StrConfigDscr(ConfigCategory.hidden, ApplyMode.apply_config, hide_in_virtual_child=True)
264
264
 
265
- path_splithttp = _StrConfigDscr(ConfigCategory.too_advanced, ApplyMode.apply_config, hide_in_virtual_child=True)
265
+ path_xhttp = _StrConfigDscr(ConfigCategory.too_advanced, ApplyMode.apply_config, hide_in_virtual_child=True)
266
266
  path_httpupgrade = _StrConfigDscr(ConfigCategory.too_advanced, ApplyMode.apply_config, hide_in_virtual_child=True)
267
267
  path_ws = _StrConfigDscr(ConfigCategory.too_advanced, ApplyMode.apply_config, hide_in_virtual_child=True)
268
268
  path_tcp = _StrConfigDscr(ConfigCategory.too_advanced, ApplyMode.apply_config, hide_in_virtual_child=True)
@@ -20,7 +20,7 @@ class ProxyTransport(StrEnum):
20
20
  tcp = auto()
21
21
  ssh = auto()
22
22
  httpupgrade = auto()
23
- splithttp = auto()
23
+ xhttp = auto()
24
24
  custom = auto()
25
25
  shadowsocks = auto()
26
26
 
@@ -143,19 +143,18 @@ class AdminstratorAdmin(AdminLTEModelView):
143
143
  """)
144
144
 
145
145
  def _max_active_users_formatter(view, context, model, name):
146
-
147
- actives = [u for u in model.recursive_users_query().all() if u.is_active]
148
- u = len(actives)
146
+ """Optimized user count formatter using database queries"""
147
+ active_count = model.recursive_users_query().filter(User.is_active == True).count()
149
148
  if model.mode == AdminMode.super_admin:
150
- return f"{u} / ∞"
149
+ return f"{active_count} / ∞"
151
150
  t = model.max_active_users
152
- rate = round(u * 100 / (t + 0.000001))
153
- state = "danger" if u >= t else ('warning' if rate > 80 else 'success')
154
- color = "#ff7e7e" if u >= t else ('#ffc107' if rate > 80 else '#9ee150')
151
+ rate = round(active_count * 100 / (t + 0.000001))
152
+ color = "#ff7e7e" if active_count >= t else ('#ffc107' if rate > 80 else '#9ee150')
153
+
155
154
  return Markup(f"""
156
155
  <div class="progress progress-lg position-relative" style="min-width: 100px;">
157
156
  <div class="progress-bar progress-bar-striped" role="progressbar" style="width: {rate}%;background-color: {color};" aria-valuenow="{rate}" aria-valuemin="0" aria-valuemax="100"></div>
158
- <span class='badge position-absolute' style="left:auto;right:auto;width: 100%;font-size:1em">{u} {_('user.home.usage.from')} {t}</span>
157
+ <span class='badge position-absolute' style="left:auto;right:auto;width: 100%;font-size:1em">{active_count} {_('user.home.usage.from')} {t}</span>
159
158
 
160
159
  </div>
161
160
  """)
@@ -16,6 +16,7 @@ from hiddifypanel.panel import hiddify, custom_widgets
16
16
  from .adminlte import AdminLTEModelView
17
17
  from hiddifypanel import hutils
18
18
 
19
+ from loguru import logger
19
20
  from flask import current_app
20
21
  # Define a custom field type for the related domains
21
22
 
@@ -152,132 +153,164 @@ class DomainAdmin(AdminLTEModelView):
152
153
 
153
154
  # TODO: refactor this function
154
155
  def on_model_change(self, form, model, is_created):
155
- model.domain = (model.domain or '').lower()
156
+ # Sanitize domain input
157
+ model.domain = (model.domain or '').lower().strip()
158
+
159
+ # Basic validation
156
160
  if model.domain == '' and model.mode != DomainType.fake:
157
161
  raise ValidationError(_("domain.empty.allowed_for_fake_only"))
158
- configs = get_hconfigs()
159
- for c in configs:
160
- if "domain" in c and c not in [ConfigEnum.decoy_domain, ConfigEnum.reality_fallback_domain] and c.category != 'hidden':
161
- if model.domain == configs[c]:
162
- raise ValidationError(_("You have used this domain in: ") + _(f"config.{c}.label"))
163
-
164
- for td in Domain.query.filter(Domain.mode == DomainType.reality, Domain.domain != model.domain).all():
165
- # print(td)
166
- if td.servernames and (model.domain in td.servernames.split(",")):
167
- raise ValidationError(_("You have used this domain in: ") + _(f"config.reality_server_names.label") + td.domain)
168
-
169
- if is_created and Domain.query.filter(Domain.domain == model.domain, Domain.child_id == model.child_id).count() > 1:
170
- raise ValidationError(_("You have used this domain in: "))
171
162
 
163
+ self._validate_not_used_before(model,is_created)
172
164
  ipv4_list = hutils.network.get_ips(4)
173
165
  ipv6_list = hutils.network.get_ips(6)
166
+ server_ips = [*ipv4_list, *ipv6_list]
174
167
 
175
- if not ipv4_list and not ipv6_list:
168
+ if not server_ips:
176
169
  raise ValidationError(_("Couldn't find your ip addresses"))
177
170
 
171
+ # Validate domain based on mode
178
172
  if "*" in model.domain and model.mode not in [DomainType.cdn, DomainType.auto_cdn_ip]:
179
173
  raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
180
174
 
181
- skip_check = "*" in model.domain or model.domain == ""
175
+ cloudflare_updated=self._update_cloudflare(model, ipv4_list,ipv6_list)
176
+
177
+ if not (cloudflare_updated or "*" in model.domain or model.domain == ""):
178
+ self._validate_domain_ips(model, server_ips)
179
+
180
+ # Handle CDN IP settings
181
+ if model.mode == DomainType.direct and model.cdn_ip:
182
+ model.cdn_ip = ""
183
+ raise ValidationError(_("Specifying CDN IP is only valid for CDN mode"))
184
+
185
+ if model.mode == DomainType.fake and not model.cdn_ip:
186
+ model.cdn_ip = str(server_ips[0])
187
+
188
+ if model.cdn_ip:
189
+ try:
190
+ hutils.network.auto_ip_selector.get_clean_ip(str(model.cdn_ip))
191
+ except Exception:
192
+ raise ValidationError(_("Error in auto cdn format"))
193
+
194
+ # Update show domains
195
+ if len(model.show_domains) == Domain.query.count():
196
+ model.show_domains = []
197
+
198
+ # Handle mode-specific settings
199
+ if model.mode == DomainType.old_xtls_direct and not hconfig(ConfigEnum.xtls_enable):
200
+ set_hconfig(ConfigEnum.xtls_enable, True)
201
+ hutils.proxy.get_proxies().invalidate_all()
202
+ elif model.mode == DomainType.reality:
203
+ self._validate_reality_settings(model, server_ips)
204
+
205
+ # Signal config update if needed
206
+ old_db_domain = Domain.by_domain(model.domain)
207
+ if is_created or not old_db_domain or old_db_domain.mode != model.mode:
208
+ # return hiddify.reinstall_action(complete_install=False, domain_changed=True)
209
+ hutils.flask.flash_config_success(restart_mode=ApplyMode.apply_config, domain_changed=True)
210
+
211
+
212
+
213
+ def _update_cloudflare(self, model, ipv4_list,ipv6_list):
182
214
  if hconfig(ConfigEnum.cloudflare) and model.mode not in [DomainType.fake, DomainType.relay, DomainType.reality]:
183
215
  try:
184
216
  proxied = model.mode in [DomainType.cdn, DomainType.auto_cdn_ip]
185
- hutils.network.cf_api.add_or_update_dns_record(model.domain, str(ipv4_list[0]), "A", proxied=proxied)
217
+ if ipv4_list:
218
+ hutils.network.cf_api.add_or_update_dns_record(model.domain, str(ipv4_list[0]), "A", proxied=proxied)
186
219
  if ipv6_list:
187
220
  hutils.network.cf_api.add_or_update_dns_record(model.domain, str(ipv6_list[0]), "AAAA", proxied=proxied)
188
-
189
- skip_check = True
221
+ return True
190
222
  except Exception as e:
191
223
  raise ValidationError(__("cloudflare.error") + f' {e}')
192
- # elif model.mode==DomainType.auto_cdn_ip:
193
- # if model.alias and not model.alias.replace("_", "").isalnum():
194
- # hutils.flask.flash(__("Using alias with special charachters may cause problem in some clients like FairVPN."), 'warning')
195
- # raise ValidationError(_("You have to add your cloudflare api key to use this feature: "))
224
+ return False
196
225
 
197
- dips = hutils.network.get_domain_ips(model.domain)
198
- server_ips = [*ipv4_list, *ipv6_list]
199
- if model.sub_link_only:
200
- if not dips:
201
- raise ValidationError(_("Domain can not be resolved! there is a problem in your domain")) # type: ignore
202
- elif not skip_check:
203
- if not dips:
204
- raise ValidationError(_("Domain can not be resolved! there is a problem in your domain")) # type: ignore
205
-
206
- domain_ip_is_same_as_panel = False
207
-
208
- for mip in server_ips:
209
- domain_ip_is_same_as_panel |= mip in dips
210
- server_ips_str = ', '.join(list(map(str, server_ips)))
211
- dips_str = ', '.join(list(map(str, dips)))
212
-
213
- if model.mode == DomainType.direct and not domain_ip_is_same_as_panel:
214
- # 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')
215
- raise ValidationError(
216
- __("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
217
-
218
- if domain_ip_is_same_as_panel and model.mode in [DomainType.cdn, DomainType.relay, DomainType.fake, DomainType.auto_cdn_ip]:
219
- # # hutils.flask.flash(__(f"In CDN mode, Domain IP={dip} should be different to your ip={', '.join(list(map(str, ipv4_list)))}"), 'warning')
220
- raise ValidationError(__("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s",
221
- server_ip=server_ips_str, domain_ip=dips_str)) # type: ignore
222
-
223
- # if model.mode in [DomainType.ss_faketls, DomainType.telegram_faketls]:
224
- # if len(Domain.query.filter(Domain.mode==model.mode and Domain.id!=model.id).all())>0:
225
- # ValidationError(f"another {model.mode} is exist")
226
-
227
- model.domain = model.domain.lower()
228
- if model.mode == DomainType.direct and model.cdn_ip:
229
- model.cdn_ip = ""
230
- raise ValidationError(f"Specifying CDN IP is only valid for CDN mode")
226
+ def _validate_reality_settings(self, model, server_ips):
227
+ """Validate REALITY protocol settings with proper error handling"""
228
+ if not hconfig(ConfigEnum.reality_enable):
229
+ set_hconfig(ConfigEnum.reality_enable, True)
230
+ hutils.proxy.get_proxies().invalidate_all()
231
231
 
232
- if model.mode == DomainType.fake and not model.cdn_ip:
233
- model.cdn_ip = str(server_ips[0])
232
+ model.servernames = (model.servernames or model.domain).lower().strip()
233
+ domains_to_check = set()
234
+ for v in [model.domain, model.servernames]:
235
+ domains_to_check.update(d.strip() for d in v.split(",") if d.strip())
234
236
 
235
- # if model.mode==DomainType.fake and model.cdn_ip!=myip:
236
- # raise ValidationError(f"Specifying CDN IP is only valid for CDN mode")
237
+ for d in domains_to_check:
238
+ # Check REALITY compatibility
239
+ if not hutils.network.is_domain_reality_friendly(d):
240
+ raise ValidationError(_("Domain is not REALITY friendly!") + f' {d}')
237
241
 
238
- # # Update the many-to-many relationship
239
- if len(model.show_domains) == Domain.query.count():
240
- model.show_domains = []
241
- if model.mode == DomainType.old_xtls_direct:
242
- if not hconfig(ConfigEnum.xtls_enable):
243
- set_hconfig(ConfigEnum.xtls_enable, True)
244
- hutils.proxy.get_proxies().invalidate_all()
245
- elif model.mode == DomainType.reality:
246
- if not hconfig(ConfigEnum.reality_enable):
247
- set_hconfig(ConfigEnum.reality_enable, True)
248
- hutils.proxy.get_proxies().invalidate_all()
249
- model.servernames = (model.servernames or model.domain).lower()
250
- for v in set([model.domain, model.servernames]):
251
- for d in v.split(","):
252
- if not d:
253
- continue
254
- if not hutils.network.is_domain_reality_friendly(d): # the minimum requirement for the REALITY protocol is to have tls1.3 and h2
255
- raise ValidationError(_("Domain is not REALITY friendly!") + f' {d}')
256
-
257
- if not hutils.network.is_in_same_asn(d, server_ips[0]):
258
- dip = next(iter(dips))
242
+ try:
243
+ if not hutils.network.is_in_same_asn(d, server_ips[0]):
244
+ domain_ips = hutils.network.get_domain_ips(d)
245
+ if domain_ips:
246
+ dip = next(iter(domain_ips))
259
247
  server_asn = hutils.network.get_ip_asn(server_ips[0])
260
- domain_asn = hutils.network.get_ip_asn(dip) # type: ignore
261
- msg = _("domain.reality.asn_issue") + \
262
- (f"<br> Server ASN={server_asn}<br>{d}_ASN={domain_asn}" if server_asn or domain_asn else "")
248
+ domain_asn = hutils.network.get_ip_asn(dip)
249
+ msg = _("domain.reality.asn_issue")
250
+ if server_asn or domain_asn:
251
+ msg += f"<br> Server ASN={server_asn}<br>{d}_ASN={domain_asn}"
263
252
  hutils.flask.flash(msg, 'warning')
253
+ except Exception as e:
254
+ logger.warning(f"ASN check failed for domain {d}: {str(e)}")
264
255
 
265
- for d in model.servernames.split(","):
266
- if not hutils.network.fallback_domain_compatible_with_servernames(model.domain, d):
267
- msg = _("REALITY Fallback domain is not compaitble with server names!") + f' {d} != {model.domain}'
268
- hutils.flask.flash(msg, 'warning')
256
+ # Check fallback compatibility
257
+ for d in model.servernames.split(","):
258
+ if d.strip() and not hutils.network.fallback_domain_compatible_with_servernames(model.domain, d):
259
+ msg = _("REALITY Fallback domain is not compatible with server names!") + f' {d} != {model.domain}'
260
+ hutils.flask.flash(msg, 'warning')
269
261
 
270
- if (model.cdn_ip):
271
- try:
272
- hutils.network.auto_ip_selector.get_clean_ip(str(model.cdn_ip))
273
- except BaseException:
274
- raise ValidationError(_("Error in auto cdn format"))
275
262
 
276
- old_db_domain = Domain.by_domain(model.domain)
277
- if is_created or not old_db_domain or old_db_domain.mode != model.mode:
278
- # return hiddify.reinstall_action(complete_install=False, domain_changed=True)
279
- hutils.flask.flash_config_success(restart_mode=ApplyMode.apply_config, domain_changed=True)
263
+ def _validate_not_used_before(self, model,is_created):
264
+ configs = get_hconfigs()
265
+ for c in configs:
266
+ if "domain" in c and c not in [ConfigEnum.decoy_domain, ConfigEnum.reality_fallback_domain] and c.category != 'hidden':
267
+ if model.domain == configs[c]:
268
+ raise ValidationError(_("You have used this domain in: ") + _(f"config.{c}.label"))
269
+
270
+ for td in Domain.query.filter(Domain.mode == DomainType.reality, Domain.domain != model.domain).all():
271
+ # print(td)
272
+ if td.servernames and (model.domain in td.servernames.split(",")):
273
+ raise ValidationError(_("You have used this domain in: ") + _(f"config.reality_server_names.label") + td.domain)
280
274
 
275
+ if is_created and Domain.query.filter(Domain.domain == model.domain, Domain.child_id == model.child_id).count() > 1:
276
+ raise ValidationError(_("You have used this domain in: "))
277
+
278
+ def _validate_domain_ips(self, model, server_ips):
279
+ """Validate domain IP resolution and matching"""
280
+
281
+ # Skip validation for wildcard or empty domains
282
+ if model.domain.startswith('*') or not model.domain:
283
+ return True
284
+
285
+ # Resolve domain IPs with timeout
286
+ try:
287
+ dips = hutils.network.get_domain_ips(model.domain, timeout=10)
288
+ except Exception as e:
289
+ logger.error(f"Error resolving domain {model.domain}: {str(e)}")
290
+ raise ValidationError(_("Domain cannot be resolved! Please check DNS settings"))
291
+
292
+ # Validate resolution success
293
+ if not dips:
294
+ raise ValidationError(_("Domain cannot be resolved! Please check DNS settings"))
295
+
296
+ # Check IP matching based on mode
297
+ domain_ip_matches_server = any(ip in dips for ip in server_ips)
298
+ server_ips_str = ', '.join(map(str, server_ips))
299
+ dips_str = ', '.join(map(str, dips))
300
+
301
+ if not domain_ip_matches_server and model.mode in [DomainType.direct]:
302
+ raise ValidationError(
303
+ __("Domain IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode",
304
+ server_ip=server_ips_str, domain_ip=dips_str))
305
+
306
+ if domain_ip_matches_server and model.mode in [DomainType.cdn, DomainType.relay, DomainType.fake, DomainType.auto_cdn_ip]:
307
+ raise ValidationError(
308
+ __("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s",
309
+ server_ip=server_ips_str, domain_ip=dips_str))
310
+
311
+ return True
312
+
313
+
281
314
  # def after_model_change(self,form, model, is_created):
282
315
  # if model.show_domains.count==0:
283
316
  # db.session.bulk_save_objects(ShowDomain(model.id,model.id))
@@ -251,11 +251,11 @@ def validate_domain(form, field):
251
251
  if dip is None:
252
252
  raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
253
253
 
254
- myip = hutils.network.get_ip(4)
255
- myip6 = hutils.network.get_ip(4)
256
- if dip and myip != dip and (not myip6 or myip6 != dip):
254
+ myips = hutils.network.get_ips()
255
+ # Fixed: Changed from get_ip(4) to get_ip(6)
256
+ if dip not in myips:
257
257
  raise ValidationError(_("Domain (%(domain)s)-> IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode",
258
- server_ip=myip, domain_ip=dip, domain=domain))
258
+ server_ip=myips, domain_ip=dip, domain=domain))
259
259
 
260
260
 
261
261
  def validate_domain_cdn(form, field):
@@ -266,10 +266,10 @@ def validate_domain_cdn(form, field):
266
266
  if dip is None:
267
267
  raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
268
268
 
269
- myip = hutils.network.get_ip(4)
270
- if myip == dip:
269
+ myips = hutils.network.get_ips()
270
+ if dip in myips:
271
271
  raise ValidationError(_("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s",
272
- server_ip=myip, domain_ip=dip, domain=domain))
272
+ server_ip=myips, domain_ip=dip, domain=domain))
273
273
 
274
274
 
275
275
  def admin_link():
@@ -27,7 +27,7 @@ from hiddifypanel import hutils
27
27
  class UserAdmin(AdminLTEModelView):
28
28
  column_default_sort = ('id', False) # Sort by username in ascending order
29
29
 
30
- column_sortable_list = ["is_active", "name", "current_usage", 'mode', "remaining_days","max_ips", "comment", 'last_online', "uuid", 'remaining_days']
30
+ column_sortable_list = ["is_active", "name", "current_usage", 'mode', "remaining_days", "max_ips", "comment", 'last_online', "uuid"]
31
31
  column_searchable_list = ["uuid", "name"]
32
32
  column_list = ["is_active", "name", "UserLinks", "current_usage", "remaining_days", "comment", 'last_online', 'mode', 'admin', "uuid"]
33
33
  column_editable_list = ["comment", "name", "uuid"]
@@ -131,7 +131,7 @@ class UserAdmin(AdminLTEModelView):
131
131
  if model.is_active:
132
132
  link = '<i class="fa-solid fa-circle-check text-success"></i> '
133
133
  elif len(model.devices):
134
- link = '<i class="fa-solid fa-users-slash text-danger" title="{_("Too many Connected IPs")}"></i>'
134
+ link = f'<i class="fa-solid fa-users-slash text-danger" title="{_("Too many Connected IPs")}"></i>'
135
135
  else:
136
136
  link = '<i class="fa-solid fa-circle-xmark text-danger"></i> '
137
137
 
@@ -161,8 +161,8 @@ class UserAdmin(AdminLTEModelView):
161
161
 
162
162
  def _usage_formatter(view, context, model, name):
163
163
  u = round(model.current_usage_GB, 3)
164
- t = round(model.usage_limit_GB, 3)
165
- rate = round(u * 100 / (t + 0.000001))
164
+ t = max(round(model.usage_limit_GB, 3), 0.001) # Prevent division by zero
165
+ rate = min(round(u * 100 / t), 100) # Cap at 100%
166
166
  state = "danger" if u >= t else ('warning' if rate > 80 else 'success')
167
167
  color = "#ff7e7e" if u >= t else ('#ffc107' if rate > 80 else '#9ee150')
168
168
  return Markup(f"""
@@ -225,38 +225,45 @@ class UserAdmin(AdminLTEModelView):
225
225
 
226
226
  def on_form_prefill(self, form, id=None):
227
227
  # print("================",form._obj.start_date)
228
- if id is None or form._obj is None or form._obj.start_date is None or form._obj.current_usage==0:
228
+ if form._obj is None:
229
+ return
230
+
231
+ if id is None or form._obj.start_date is None or form._obj.current_usage==0:
229
232
  msg = _("Package not started yet.")
230
233
  # form.reset['class']="d-none"
231
- if form._obj.start_date is None:
234
+ if form._obj.start_date is None:
235
+ if hasattr(form, 'reset_days'):
232
236
  delattr(form, 'reset_days')
233
- if form._obj.current_usage==0:
234
- delattr(form, 'reset_usage')
235
- # delattr(form,'disable_user')
236
237
  else:
237
- remaining = form._obj.remaining_days # remaining_days(form._obj)
238
+ remaining = form._obj.remaining_days
238
239
  relative_remaining = hutils.convert.format_timedelta(datetime.timedelta(days=remaining))
239
240
  msg = _("Remaining about %(relative)s, exactly %(days)s days", relative=relative_remaining, days=remaining)
240
241
  form.reset_days.label.text += f" ({msg})"
241
- usr_usage = f" ({_('user.home.usage.title')} {round(form._obj.current_usage_GB,3)}GB)"
242
- form.reset_usage.label.text += usr_usage
243
- form.reset_usage.data = False
244
242
  form.reset_days.data = False
245
243
 
246
- form.usage_limit.label.text += usr_usage
247
-
248
- # if form._obj.mode==UserMode.disable:
249
- # delattr(form,'disable_user')
250
- # form.disable_user.data=form._obj.mode==UserMode.disable
251
- if form._obj.start_date:
252
- started = form._obj.start_date - datetime.date.today()
253
- msg = _("Started from %(relative)s", relative=hutils.convert.format_timedelta(started))
254
- form.package_days.label.text += f" ({msg})"
255
- if started.days <= 0:
256
- exact_start = _("Started %(days)s days ago", days=-started.days)
257
- else:
258
- exact_start = _("Will Start in %(days)s days", days=started.days)
259
- form.package_days.description += f" ({exact_start})"
244
+ # Handle reset_usage field
245
+ if form._obj.current_usage == 0:
246
+ if hasattr(form, 'reset_usage'):
247
+ delattr(form, 'reset_usage')
248
+ else:
249
+ usr_usage = f" ({_('user.home.usage.title')} {round(form._obj.current_usage_GB,3)}GB)"
250
+ if hasattr(form, 'reset_usage'):
251
+ form.reset_usage.label.text += usr_usage
252
+ form.reset_usage.data = False
253
+
254
+ if hasattr(form, 'usage_limit'):
255
+ form.usage_limit.label.text += usr_usage
256
+
257
+ # Handle package days info
258
+ if form._obj.start_date and hasattr(form, 'package_days'):
259
+ started = form._obj.start_date - datetime.date.today()
260
+ msg = _("Started from %(relative)s", relative=hutils.convert.format_timedelta(started))
261
+ form.package_days.label.text += f" ({msg})"
262
+ if started.days <= 0:
263
+ exact_start = _("Started %(days)s days ago", days=-started.days)
264
+ else:
265
+ exact_start = _("Will Start in %(days)s days", days=started.days)
266
+ form.package_days.description += f" ({exact_start})"
260
267
 
261
268
  def get_edit_form(self):
262
269
  form = super().get_edit_form()
@@ -267,26 +274,44 @@ class UserAdmin(AdminLTEModelView):
267
274
  return form
268
275
 
269
276
  def on_model_change(self, form, model, is_created):
270
- model.max_ips = max(3, model.max_ips or 10000)
277
+ # Validate max_ips
278
+ try:
279
+ model.max_ips = max(3, min(int(model.max_ips or 10000), 10000))
280
+ except (ValueError, TypeError):
281
+ model.max_ips = 1000
282
+
283
+ # Show donation message
271
284
  if len(User.query.all()) % 4 == 0:
272
285
  hutils.flask.flash(('<div id="show-modal-donation"></div>'), ' d-none')
286
+
287
+ # Validate UUID
273
288
  if not re.match("^[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}$", model.uuid):
274
289
  raise ValidationError('Invalid UUID e.g.,' + str(uuid.uuid4()))
290
+
291
+ # Handle reset flags
275
292
  if hasattr(form, 'reset_usage') and form.reset_usage.data:
276
293
  model.current_usage_GB = 0
277
- # if model.telegram_id and model.telegram_id != '0' and not re.match(r"^[1-9]\d*$", model.telegram_id):
278
- # raise ValidationError('Invalid Telegram ID')
279
- # if form.disable_user.data:
280
- # model.mode=UserMode.disable
294
+
281
295
  if hasattr(form, 'reset_days') and form.reset_days.data:
282
296
  model.start_date = None
283
- model.package_days = min(model.package_days, 10000)
297
+
298
+ # Validate package days
299
+ try:
300
+ model.package_days = min(int(model.package_days), 10000)
301
+ except (ValueError, TypeError):
302
+ model.package_days = 10000
303
+
304
+ # Handle user ownership
284
305
  old_user = User.by_id(model.id)
285
306
  if not model.added_by or model.added_by == 1:
286
307
  model.added_by = g.account.id
308
+
309
+ # Validate user limits
287
310
  if not g.account.can_have_more_users():
288
311
  raise ValidationError(_('You have too much users! You can have only %(active)s active users and %(total)s users',
289
312
  active=g.account.max_active_users, total=g.account.max_users))
313
+
314
+ # Handle UUID changes
290
315
  if old_user and old_user.uuid != model.uuid:
291
316
  user_driver.remove_client(old_user)
292
317
 
@@ -192,10 +192,12 @@
192
192
  }
193
193
 
194
194
  function update_from_json(data) {
195
- usage_history = data['usage_history']
196
- onlines = usage_history['m5']['online']
197
- total_users = usage_history['total']['users']
198
- stats = data['stats']
195
+ // Use local variables instead of globals
196
+ const usage_history = data['usage_history'];
197
+ const onlines = usage_history['m5']['online'];
198
+ const total_users = usage_history['total']['users'];
199
+ const stats = data['stats'];
200
+
199
201
  info_box("today", "fa-solid fa-calendar", "Today Usage",
200
202
  ((usage_history['today']['usage'] / Math.pow(1024, 3)).toFixed(1)) + " GB",
201
203
  usage_history['today']['online'] / Math.max(1, total_users) * 100,
@@ -121,9 +121,17 @@
121
121
  $(document).Toasts('create', {
122
122
  class: 'bg-danger',
123
123
  position: "{{'topRight' if get_locale()=='fa' else 'topLeft'}}",
124
- title: status + " " + error,
125
- body: JSON.stringify(jqx)
126
- })
124
+ title: $('<div>').text(status + " " + error).html(),
125
+ body: $('<div>').text(JSON.stringify(jqx)).html(),
126
+ autohide: true,
127
+ delay: 5000
128
+ });
129
+
130
+ console.error('API Error:', {
131
+ status: status,
132
+ error: error,
133
+ details: jqx
134
+ });
127
135
  },
128
136
  success: function (data) {
129
137
  // dialog.modal('hide');
@@ -2,6 +2,7 @@ import telebot
2
2
  from flask import request
3
3
  from apiflask import abort
4
4
  from flask_restful import Resource
5
+ import time
5
6
 
6
7
  from hiddifypanel.models import *
7
8
  from hiddifypanel import Events
@@ -11,7 +12,24 @@ logger = telebot.logger
11
12
 
12
13
  class ExceptionHandler(telebot.ExceptionHandler):
13
14
  def handle(self, exception):
14
- logger.error(exception)
15
+ """Improved error handling for Telegram bot exceptions"""
16
+ error_msg = str(exception)
17
+ logger.error(f"Telegram bot error: {error_msg}")
18
+
19
+ try:
20
+ # Attempt recovery based on error type
21
+ if "webhook" in error_msg.lower():
22
+ if hasattr(bot, 'remove_webhook'):
23
+ bot.remove_webhook()
24
+ logger.info("Removed webhook due to error")
25
+ elif "connection" in error_msg.lower():
26
+ # Wait and retry for connection issues
27
+ time.sleep(5)
28
+ return True # Indicates retry
29
+ except Exception as e:
30
+ logger.error(f"Error during recovery attempt: {str(e)}")
31
+
32
+ return False # Don't retry for unknown errors
15
33
 
16
34
 
17
35
  bot = telebot.TeleBot("1:2", parse_mode="HTML", threaded=False, exception_handler=ExceptionHandler())
@@ -77,7 +77,7 @@ def _v94(child_id):
77
77
 
78
78
  def _v93(child_id):
79
79
  set_hconfig(ConfigEnum.quic_enable, True)
80
- set_hconfig(ConfigEnum.splithttp_enable, True)
80
+ set_hconfig(ConfigEnum.xhttp_enable, True)
81
81
 
82
82
 
83
83
  def _v92(child_id):
@@ -85,9 +85,9 @@ def _v92(child_id):
85
85
 
86
86
 
87
87
  def _v89(child_id):
88
- set_hconfig(ConfigEnum.path_splithttp,
88
+ set_hconfig(ConfigEnum.path_xhttp,
89
89
  hutils.random.get_random_string(7, 15))
90
- set_hconfig(ConfigEnum.splithttp_enable, False)
90
+ set_hconfig(ConfigEnum.xhttp_enable, False)
91
91
  pass
92
92
 
93
93
 
@@ -218,7 +218,7 @@ def _v65():
218
218
  add_config_if_not_exist(ConfigEnum.mux_min_streams, '4')
219
219
  add_config_if_not_exist(ConfigEnum.mux_max_streams, '0')
220
220
  add_config_if_not_exist(ConfigEnum.mux_padding_enable, False)
221
- add_config_if_not_exist(ConfigEnum.mux_brutal_enable, True)
221
+ add_config_if_not_exist(ConfigEnum.mux_brutal_enable, False)
222
222
  add_config_if_not_exist(ConfigEnum.mux_brutal_up_mbps, '100')
223
223
  add_config_if_not_exist(ConfigEnum.mux_brutal_down_mbps, '100')
224
224
 
@@ -571,9 +571,9 @@ def get_proxy_rows_v1():
571
571
  "httpupgrade direct vless",
572
572
  # "httpupgrade direct trojan",
573
573
  "httpupgrade direct vmess",
574
- "splithttp direct vless",
575
- "splithttp direct trojan",
576
- "splithttp direct vmess",
574
+ "xhttp direct vless",
575
+ "xhttp direct trojan",
576
+ "xhttp direct vmess",
577
577
  "tcp direct vless",
578
578
  "tcp direct trojan",
579
579
  "tcp direct vmess",
@@ -591,9 +591,9 @@ def get_proxy_rows_v1():
591
591
  # "httpupgrade relay trojan",
592
592
  "httpupgrade relay vmess",
593
593
 
594
- "splithttp relay vless",
595
- "splithttp relay trojan",
596
- "splithttp relay vmess",
594
+ "xhttp relay vless",
595
+ "xhttp relay trojan",
596
+ "xhttp relay vmess",
597
597
 
598
598
  "tcp relay vless",
599
599
  "tcp relay trojan",
@@ -615,9 +615,9 @@ def get_proxy_rows_v1():
615
615
  # "httpupgrade CDN trojan",
616
616
  "httpupgrade CDN vmess",
617
617
 
618
- "splithttp CDN vless",
619
- "splithttp CDN trojan",
620
- "splithttp CDN vmess",
618
+ "xhttp CDN vless",
619
+ "xhttp CDN trojan",
620
+ "xhttp CDN vmess",
621
621
 
622
622
  "grpc CDN vless",
623
623
  "grpc CDN trojan",
@@ -663,7 +663,7 @@ def make_proxy_rows(cfgs):
663
663
  for l3 in [ProxyL3.h3_quic, "tls_h2", "tls", "http", "reality"]:
664
664
  for c in cfgs:
665
665
  transport, cdn, proto = c.split(" ")
666
- if transport != ProxyTransport.splithttp and l3 == ProxyL3.h3_quic:
666
+ if transport != ProxyTransport.xhttp and l3 == ProxyL3.h3_quic:
667
667
  continue
668
668
  if l3 in ["kcp", 'reality'] and cdn != "direct":
669
669
  continue
@@ -750,8 +750,11 @@ def add_new_enum_values():
750
750
  continue
751
751
 
752
752
  # Add the new value to the enum column in the database
753
- enumstr = ','.join([f"'{a}'" for a in [*existing_values, *old_values]])
754
-
753
+ # enumstr = ','.join([f"'{a}'" for a in [*existing_values, *old_values]])
754
+ enumstr = ','.join([f"'{a}'" for a in [*existing_values]])
755
+ expired_enumstr = ','.join([f"'{a}'" for a in [*old_values]])
756
+ db_execute(
757
+ f"delete from {table_name} where `{column_name}` in ({expired_enumstr});", commit=True)
755
758
  db_execute(
756
759
  f"ALTER TABLE {table_name} MODIFY COLUMN `{column_name}` ENUM({enumstr});", commit=True)
757
760
 
@@ -642,7 +642,7 @@
642
642
  "description": "ça devrait être aléatoire",
643
643
  "label": "ℹ️ Chemin de mise à niveau HTTP"
644
644
  },
645
- "path_splithttp": {
645
+ "path_xhttp": {
646
646
  "description": "Cette option définit le chemin pour Split HTTP",
647
647
  "label": "Chemin pour le HTTP fractionné"
648
648
  },
@@ -778,7 +778,7 @@
778
778
  "description": "Autorisez vos utilisateurs à effectuer des tests de vitesse. Cela les aide à identifier la qualité du lien",
779
779
  "label": "🚀 Test de vitesse"
780
780
  },
781
- "splithttp_enable": {
781
+ "xhttp_enable": {
782
782
  "description": "Ce protocole envoie et reçoit des informations dans des canaux individuels ",
783
783
  "label": "🈁 Split HTTP"
784
784
  },
@@ -642,7 +642,7 @@
642
642
  "description": "ကျပန်းဖြစ်သင့်သည်",
643
643
  "label": "ℹ️ HTTP အဆင့်မြှင့်တင်မှု လမ်းကြောင်း"
644
644
  },
645
- "path_splithttp": {
645
+ "path_xhttp": {
646
646
  "description": "ဤရွေးချယ်မှုသည် Split HTTP အတွက် လမ်းကြောင်းကို သတ်မှတ်သည်။",
647
647
  "label": "ℹ️ Split HTTP အတွက် လမ်းကြောင်း"
648
648
  },
@@ -778,7 +778,7 @@
778
778
  "description": "သင့်အသုံးပြုသူများကို အမြန်နှုန်းစမ်းသပ်မှု ပြုလုပ်ခွင့်ပြုရန်။ ၎င်းသည် ၎င်းတို့အား လင့်ခ်အရည်အသွေးကို ခွဲခြားသတ်မှတ်ရန် ကူညီပေးသည်။",
779
779
  "label": "🚀 အမြန်နှုန်း စမ်းသပ်"
780
780
  },
781
- "splithttp_enable": {
781
+ "xhttp_enable": {
782
782
  "description": "ဤပရိုတိုကောသည် ချန်နယ်တစ်ခုချင်းစီတွင် အချက်အလက်များကို ပေးပို့လက်ခံသည်",
783
783
  "label": "🈁 Split HTTP"
784
784
  },
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hiddifypanel
3
- Version: 10.80.0.dev13
3
+ Version: 10.80.0.dev14
4
4
  Summary: hiddifypanel multi proxy panel
5
5
  Home-page: https://hiddify.com
6
6
  License: LICENSE.md
@@ -1,6 +1,6 @@
1
1
  hiddifypanel/Events.py,sha256=AlnRdjVul0jP-NCT4-zoaQgowoOo-JhdQB4ytetAFKA,723
2
- hiddifypanel/VERSION,sha256=PKspHVtSTTQOTDBwb16fiGq-Oh5ypRmp7xsRS5xUsJo,14
3
- hiddifypanel/VERSION.py,sha256=IH6TWOHTBrpGWNcWSD4EFMh7SoYgoNJxiK-f_lt1Li8,227
2
+ hiddifypanel/VERSION,sha256=xBTCiBOLHDQLmnkIj8kG6LekkaQVA9EDbrLn_tKtU78,14
3
+ hiddifypanel/VERSION.py,sha256=zzfFvEjn3EVsQaXeZlJR3pKOPBTBKtsIquwH3ds11-8,227
4
4
  hiddifypanel/__init__.py,sha256=kigwDO8d9jXyPZLvJAWd6zo-GX3pG_xWf-q2aStz80Y,377
5
5
  hiddifypanel/__main__.py,sha256=IVchnXpK6bm8T3N--mN17HBQNLMeLAjyP7iwzULexB4,218
6
6
  hiddifypanel/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -39,11 +39,11 @@ hiddifypanel/hutils/node/child.py,sha256=oAyKlEHHn9FHcpZ9jBi3nYH-GHs8H7Gi2hzkmtO
39
39
  hiddifypanel/hutils/node/parent.py,sha256=UbyfvfP4fTSn6HN9oZDjYsKYIejiqW6eApKIfP0Mz5U,3749
40
40
  hiddifypanel/hutils/node/shared.py,sha256=FDSj3e-i3pb3mEv5vcUeX0Km1nxYg1CeAruIq7RwFmU,2540
41
41
  hiddifypanel/hutils/proxy/__init__.py,sha256=xXBa83kjYT_b-BNseEykfQYyJBQHTq1ZosfR8ZrQHkI,106
42
- hiddifypanel/hutils/proxy/clash.py,sha256=7Qs2b9KCV3uzku_QWAgKNDGwW1k5bRyKVutYO1NBhfA,7073
43
- hiddifypanel/hutils/proxy/shared.py,sha256=C9DSa1LxeEP8jB5pP-p8OyPU4-QiD7guPwqSxSX7dbM,22227
44
- hiddifypanel/hutils/proxy/singbox.py,sha256=B-MXmd4dGMkD_hbr1cE01N1MgP7khD4suq1SPntBkZ0,11726
45
- hiddifypanel/hutils/proxy/xray.py,sha256=MYcS-kxh3P5soG3G90FeVxrSvfQVDfgB4_7UWQQ14gM,10816
46
- hiddifypanel/hutils/proxy/xrayjson.py,sha256=kdM1tEH1fGEVW0wk6EquPSL_5kBmbkqiYOBvQeoWnwU,15006
42
+ hiddifypanel/hutils/proxy/clash.py,sha256=V9Y2UIw-CYTXD_Q73Oeq3WLw6chsPrMIiNxYnlWyNbg,7065
43
+ hiddifypanel/hutils/proxy/shared.py,sha256=Zg4qMqp-fzWXHMqZsE1stluVDgT71AlFVlJAxQwMtfQ,22203
44
+ hiddifypanel/hutils/proxy/singbox.py,sha256=Fmmzoake-gpnRB5yfTyQvd1dB-10WKwhJt4vhiKzJZQ,11722
45
+ hiddifypanel/hutils/proxy/xray.py,sha256=0vEHL9yq5Si7W_fgI8tn9zwNAWhwBE7djun05cAUDF8,10808
46
+ hiddifypanel/hutils/proxy/xrayjson.py,sha256=iEfMlLZfzYzp5eCj8AuL9nXmuPJavKZaoAjtASEIhNY,14990
47
47
  hiddifypanel/hutils/random.py,sha256=KrsarmRNL05PYzwMxDaDyv-_QcKS0YsZR2z7BnllAqI,1789
48
48
  hiddifypanel/hutils/system.py,sha256=nX7ZvmXKfHu6_cFVOGZRG-7ch2glqgzQL2iWraQc4S0,4350
49
49
  hiddifypanel/hutils/utils.py,sha256=qOvyBFQxBFAV9HYtZn8XVgIauNbVIE2A6WGSpf-NyQM,2349
@@ -52,27 +52,27 @@ hiddifypanel/models/admin.py,sha256=Ocvb9x1LG2rG36euoSoVvxUXf1LPBLieEPTghcXjOBk,
52
52
  hiddifypanel/models/base_account.py,sha256=RkdGy6KmbX1E-VDNtVNE2RYxhvPuWg18Grm5H9Dv1-k,3561
53
53
  hiddifypanel/models/child.py,sha256=ZFJaH-GWTKAGD0BGMH0iKEMipi37_cAk59OeJKt2IKA,3039
54
54
  hiddifypanel/models/config.py,sha256=goWQugd1yelIP3z1nkTkh4Uvh4Ak1rEXS33Ru_kTpsg,6373
55
- hiddifypanel/models/config_enum.py,sha256=tE22TGm0alr8QZQi6EDXrzCDeBBSgrVXl3y_F9kMzEE,16713
55
+ hiddifypanel/models/config_enum.py,sha256=ZhO9K7IAfa3J-eNmAzToduz0h37lh40dP37tr1kPqnE,16705
56
56
  hiddifypanel/models/domain.py,sha256=0tAPHR6XukN35CoyOxR3zXDNjXR-w_Ezd89jxk2H-xc,8308
57
57
  hiddifypanel/models/parent_domain.py,sha256=bs5F1neOAQu9XHEk3QQTBM4p2iuebM4cnAQqwfNjCtg,2291
58
- hiddifypanel/models/proxy.py,sha256=czKeDz_MVUSHuX2Lf9H2Ys_kgkggtzEiUx3HZPjJCXQ,3327
58
+ hiddifypanel/models/proxy.py,sha256=Ha_gz7RHrPVeP7wDjkOaWeRN99XlITMklDTSzBXGlY8,3323
59
59
  hiddifypanel/models/report.py,sha256=33h9k12SAEWkwZsc3-jUdIIpFL66cEOTHQqVXd07NWY,1001
60
60
  hiddifypanel/models/role.py,sha256=V93_AhOcgbIiAaRYUaNIWKsKZ704ANl0hq-uAJFQCUo,269
61
61
  hiddifypanel/models/usage.py,sha256=BCZtYhmgru-bF8O2dGwJgcN7qtZ29a3arQEK9EVPdtE,4362
62
62
  hiddifypanel/models/user.py,sha256=GXVcxP1kgAIqb0OLC_6vhu4kKMTkS5OBT7oPrfD59hg,14802
63
63
  hiddifypanel/panel/__init__.py,sha256=q7y-nJntWhWX7dwDm3q-QWT5ZNytT3y6IJsdGtH2B6c,196
64
64
  hiddifypanel/panel/admin/Actions.py,sha256=o_ENbphriVrbRJkx9nvrkpaliuMIfp34sscMkZJ3P5s,8578
65
- hiddifypanel/panel/admin/AdminstratorAdmin.py,sha256=n2Ma-bWmSKy004u_Fkz4TMsHQ-7y_6HPDAsRtolKp_0,10457
65
+ hiddifypanel/panel/admin/AdminstratorAdmin.py,sha256=X8MI3DtW62vJqFRp97M_CxSdB-NFNMlZOSDsd5hn8HA,10482
66
66
  hiddifypanel/panel/admin/Backup.py,sha256=BKSoAZgw1j16P1Jh9vMqGj7ZfB2m-WafDK0C5vil5FY,3634
67
67
  hiddifypanel/panel/admin/ConfigAdmin.py,sha256=0hnLY-8BxrpVnrAcQaedWjHnRUq1X_Styi_ZCZ2ivds,2876
68
68
  hiddifypanel/panel/admin/Dashboard.py,sha256=JOqZLHxPOYKQYQVJ7AtHAkilH-anJZQyK1rQrgCJUeA,3798
69
- hiddifypanel/panel/admin/DomainAdmin.py,sha256=0g9AEk_YVCEZZMscgVDLsqQAAk0QBMW1h6Tt2kW2yS4,15829
69
+ hiddifypanel/panel/admin/DomainAdmin.py,sha256=PQv1U_Ro0VMRjX4dPXUewiHJuZdi1ocvA1jw4mgOSdo,16152
70
70
  hiddifypanel/panel/admin/NodeAdmin.py,sha256=QAHQjF7e7F4KqsWNWpMt7SoLANlFEborVtWQV9OXJ2E,3102
71
71
  hiddifypanel/panel/admin/ProxyAdmin.py,sha256=HtuYHkZ8LCrYtjF2xO2i7lyw_KtOTBYCPu0bNWR4fOs,5364
72
- hiddifypanel/panel/admin/QuickSetup.py,sha256=o9-zgVuyySlHFDKR4r3BBuuGbTVfnjcsmC0BjPBuQ78,12661
72
+ hiddifypanel/panel/admin/QuickSetup.py,sha256=wma8Ctka4PM80B0UJY2JXzl0cJnaBkcyAh1__pfUffo,12645
73
73
  hiddifypanel/panel/admin/SettingAdmin.py,sha256=SCzhsZh9QrmYNPPhutQfMRuB_q6RNCXv9hy-uLjlrd0,19893
74
74
  hiddifypanel/panel/admin/Terminal.py,sha256=rzZWRjMhjVnAvC65rfE3HJT3boUDznI6fl-htzKp7sI,1712
75
- hiddifypanel/panel/admin/UserAdmin.py,sha256=4soRZqc75b92P2Q4YHVgtXkgDKa2yO4R8vrTq3MOwmY,18175
75
+ hiddifypanel/panel/admin/UserAdmin.py,sha256=i1-B0iBtFA-D18V920hFlYJIzPdiuYUKtLP860D0sbY,18601
76
76
  hiddifypanel/panel/admin/__init__.py,sha256=hb0A2HuK_nBZRCNPumwahXX-25FMxODKYlNbk2ItF08,3015
77
77
  hiddifypanel/panel/admin/adminlte.py,sha256=TrUUu6WYUM-rpgvW2C2KBq3bXkvKe3Pa3cEPjpWCJuk,758
78
78
  hiddifypanel/panel/admin/commercial_info.py,sha256=_fBJcR6zTlMs5Wx4NQJYxq5LvMHY8qfoh73-eCNJyb8,278
@@ -109,12 +109,12 @@ hiddifypanel/panel/admin/templates/fake_for_translation/Flask-AdminLTE3/template
109
109
  hiddifypanel/panel/admin/templates/fake_for_translation/Flask-AdminLTE3/templates/flask-admin/rediscli/console.html,sha256=m0lQD8cwGUMfzRjiPeGFlV3cdKqtArxhSzPXOBd7myM,909
110
110
  hiddifypanel/panel/admin/templates/fake_for_translation/Flask-AdminLTE3/templates/flask-admin/rediscli/response.html,sha256=FekGe9v4RIMoGZlnk9TwqOFjSUVaRweLeZf-oFCVOck,965
111
111
  hiddifypanel/panel/admin/templates/fake_for_translation/Flask-AdminLTE3/templates/flask-admin/static.html,sha256=S-_WCUQPTU4zWO7C2fJ8uxf47Q7QroSrKkIHstBDfTU,94
112
- hiddifypanel/panel/admin/templates/index.html,sha256=4QPq8ATHmzaEpMjLkxCRpTTaZZ1XN4Z1ve87ODQCgl4,13811
112
+ hiddifypanel/panel/admin/templates/index.html,sha256=aFNSYWav3qI4kLhdTuk_yTPJc5RLY-9H-3NtM8k_HV0,13898
113
113
  hiddifypanel/panel/admin/templates/ltemaster.html,sha256=KE_HsUOIR__BG1fW8DZ05rhgckQ8ls0a-ho6dWtmuX4,1080
114
114
  hiddifypanel/panel/admin/templates/model/admin_list.html,sha256=lpjiAl7kR5xb0fLpI5-kmf9uA6CVSAFuNE-2WZ152yQ,690
115
115
  hiddifypanel/panel/admin/templates/model/domain_list.html,sha256=gl-LnCQCPve5OHH0ber0bwV9vAguZB_PXv0qgpx0qaE,288
116
116
  hiddifypanel/panel/admin/templates/model/proxydetail_list.html,sha256=5VVFPZkcGhfKVq842CWggv1Wte6CQodrbGxO4AJ9Bo0,206
117
- hiddifypanel/panel/admin/templates/model/user_list.html,sha256=TNmANkDiQWDrwXPoD_u-Oo9sswg9XlWUOJcdFfTkd1o,5790
117
+ hiddifypanel/panel/admin/templates/model/user_list.html,sha256=XwrcElWgJhLViOGlowSNLPW-SX6wk9Jd5_Uy6n11d6Q,6158
118
118
  hiddifypanel/panel/admin/templates/parent_dash.html,sha256=kAVBwv287oaQFfNmvTxBqgaZOaAf1Nz6jPXfEZMfTXQ,1814
119
119
  hiddifypanel/panel/admin/templates/proxy.html,sha256=Q-Flp_O1LuuJnwWbodte2yr96MGzDoqgU50kB4dcsf0,1777
120
120
  hiddifypanel/panel/admin/templates/quick_setup.html,sha256=tYZnRBH682eThl6lQcnLNKIdCQDTWRZWccIZSiNxSlA,2101
@@ -133,7 +133,7 @@ hiddifypanel/panel/commercial/__init__.py,sha256=oanPV36n6MXiVhiq_NYL6K9ENhQXZGQ
133
133
  hiddifypanel/panel/commercial/restapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
134
  hiddifypanel/panel/commercial/restapi/v1/__init__.py,sha256=p7KxjATIX71uT1JQJrg9LKn5RgYJS2k6urX4MlOCb7E,1229
135
135
  hiddifypanel/panel/commercial/restapi/v1/resources.py,sha256=D7HTLAW-KA_Ikm9VArN2FSARfyCPKHxr3WEs_y4_HTM,4691
136
- hiddifypanel/panel/commercial/restapi/v1/tgbot.py,sha256=uRPGg96A3rUDaIrB3sQuSSIY66DYZnPkTuCQfXyITqk,2370
136
+ hiddifypanel/panel/commercial/restapi/v1/tgbot.py,sha256=nBj-BZ-BTocLPFY7VK2R1Cae_xCSJZUah0TDhEzF0n0,3132
137
137
  hiddifypanel/panel/commercial/restapi/v1/tgmsg.py,sha256=bUERY88cNvcDQ-SxoL4oNn6fiIKj8souyS1kSYnQmuU,3033
138
138
  hiddifypanel/panel/commercial/restapi/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
139
  hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py,sha256=Q0nvBCzTIHb0BNTBUjbVN2_j4e6MJhDWHE6_RNBleso,1871
@@ -181,7 +181,7 @@ hiddifypanel/panel/common_bp/templates/login.html,sha256=jDl9-Nh2qMuCsLQmXm7e5jv
181
181
  hiddifypanel/panel/custom_widgets.py,sha256=_zA0WZRZOCyh6Z1gW62aRQLMAOM_m85B2oZoIOU59Ys,2637
182
182
  hiddifypanel/panel/hiddify.py,sha256=-GBmkEXnGsIhZtjRomYJsqmnEToZwSXzd8ltS6Slgrc,15615
183
183
  hiddifypanel/panel/hlogger.py,sha256=1L2T1fwuozPoNfkcKF-wP2TH2pd7lGRr9htWqwR1c2M,1157
184
- hiddifypanel/panel/init_db.py,sha256=w3iV9xVU-VfHSad9KnppvwwFKMoS1bUske_PTm3EfEA,37098
184
+ hiddifypanel/panel/init_db.py,sha256=fgKZ27xFb1p1m3vqph3EUv6Gh7_fqFCaTmbWFNWAJY0,37305
185
185
  hiddifypanel/panel/node/__init__.py,sha256=gGwtD5XczIpB1OP7Fb3JLxb8kGxn5zNgeHQ-MgGg01g,189
186
186
  hiddifypanel/panel/node/a.py,sha256=KTzfA-RYRGNGtjFlXcYgH7N1gJRqrdneksXr0XmivvU,330
187
187
  hiddifypanel/panel/node/hello.py,sha256=P6htWmqwk7uixDrl0bSgRocV_zlatSBNAx_RnivN0aQ,519
@@ -850,13 +850,13 @@ hiddifypanel/translations/zh/LC_MESSAGES/messages.mo,sha256=WDVuh5nD9Lqzocm43KAU
850
850
  hiddifypanel/translations/zh/LC_MESSAGES/messages.po,sha256=r9ekUU5AcPRWMYHkEyiBQWoBWw5KWGDrfU4IXhd50CE,62592
851
851
  hiddifypanel/translations.i18n/en.json,sha256=jNo3qUHuEZ_Q5CXuP3K6DRM7Nih0DSdMcjoEenDFAJQ,56637
852
852
  hiddifypanel/translations.i18n/fa.json,sha256=MoLFtZq9JhlV3mT1GIL0NtIrVlnu4iR_cMyW6C0dvAw,78581
853
- hiddifypanel/translations.i18n/fr.json,sha256=WlSMqMD_xSrg0APYlWu9Jo5y4th_hKSMOUacDlPkFKI,80850
854
- hiddifypanel/translations.i18n/my.json,sha256=Z_MIHqJU3eos9lZCr4UDrcAZkiimOp5qjle9Brz_BP0,136293
853
+ hiddifypanel/translations.i18n/fr.json,sha256=3D_JxOMxuQrdDCTTvsah5VXxvYcPcZFzBzeHqQZwRuI,80842
854
+ hiddifypanel/translations.i18n/my.json,sha256=UHXP_EuyX9f5mEAtcxp_40TgGykXs_ap9yp7QcsVQ88,136285
855
855
  hiddifypanel/translations.i18n/pt.json,sha256=utRxsg-gt8TZAsjszZmr-2cy0gH0LQ5CavIyQqqgzy0,59165
856
856
  hiddifypanel/translations.i18n/ru.json,sha256=J77yrIPYj0DKzqwvcyOlDfmr8mJsm2Phu9UtveBdw5Q,82933
857
857
  hiddifypanel/translations.i18n/zh.json,sha256=f8T19F92fa0FyexbgSLdRItPZcKIqBXnzzXLW77s3eE,54843
858
- hiddifypanel-10.80.0.dev13.dist-info/LICENSE.md,sha256=oDrt-cUsyiDGnRPjEJh-3dH2ddAuK_bIVBD8ntkOtZw,19807
859
- hiddifypanel-10.80.0.dev13.dist-info/METADATA,sha256=ZEHOia85NHlo5_d9fgADKScdQMQlCqsVBqdMNBPV1jg,3963
860
- hiddifypanel-10.80.0.dev13.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
861
- hiddifypanel-10.80.0.dev13.dist-info/entry_points.txt,sha256=fiVgmdZ7nff9Ow1XnyMFrn1y4akk9gwnDkxpN8P3Xrw,59
862
- hiddifypanel-10.80.0.dev13.dist-info/RECORD,,
858
+ hiddifypanel-10.80.0.dev14.dist-info/LICENSE.md,sha256=oDrt-cUsyiDGnRPjEJh-3dH2ddAuK_bIVBD8ntkOtZw,19807
859
+ hiddifypanel-10.80.0.dev14.dist-info/METADATA,sha256=ls5SsP-LsOn6leZ7zqi52nfOX6HBLYMGVIHjvavyC7w,3963
860
+ hiddifypanel-10.80.0.dev14.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
861
+ hiddifypanel-10.80.0.dev14.dist-info/entry_points.txt,sha256=fiVgmdZ7nff9Ow1XnyMFrn1y4akk9gwnDkxpN8P3Xrw,59
862
+ hiddifypanel-10.80.0.dev14.dist-info/RECORD,,