hiddifypanel 10.20.3__py3-none-any.whl → 10.30.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/base.py +17 -8
  4. hiddifypanel/cache.py +2 -51
  5. hiddifypanel/drivers/wireguard_api.py +24 -5
  6. hiddifypanel/hutils/convert.py +1 -1
  7. hiddifypanel/hutils/flask.py +28 -2
  8. hiddifypanel/hutils/importer/xui.py +6 -7
  9. hiddifypanel/hutils/network/__init__.py +1 -0
  10. hiddifypanel/hutils/network/cf_api.py +84 -0
  11. hiddifypanel/hutils/network/net.py +26 -49
  12. hiddifypanel/hutils/node/child.py +25 -7
  13. hiddifypanel/hutils/node/parent.py +7 -7
  14. hiddifypanel/hutils/node/shared.py +19 -6
  15. hiddifypanel/hutils/proxy/clash.py +1 -1
  16. hiddifypanel/hutils/proxy/shared.py +1 -1
  17. hiddifypanel/hutils/proxy/singbox.py +2 -3
  18. hiddifypanel/hutils/proxy/xray.py +12 -10
  19. hiddifypanel/hutils/proxy/xrayjson.py +26 -49
  20. hiddifypanel/hutils/utils.py +47 -3
  21. hiddifypanel/models/__init__.py +1 -1
  22. hiddifypanel/models/admin.py +9 -2
  23. hiddifypanel/models/base_account.py +3 -1
  24. hiddifypanel/models/config.py +5 -7
  25. hiddifypanel/models/config_enum.py +18 -6
  26. hiddifypanel/models/domain.py +82 -118
  27. hiddifypanel/models/user.py +44 -24
  28. hiddifypanel/panel/admin/Actions.py +6 -11
  29. hiddifypanel/panel/admin/AdminstratorAdmin.py +3 -9
  30. hiddifypanel/panel/admin/Backup.py +5 -8
  31. hiddifypanel/panel/admin/Dashboard.py +3 -4
  32. hiddifypanel/panel/admin/DomainAdmin.py +20 -15
  33. hiddifypanel/panel/admin/ProxyAdmin.py +3 -10
  34. hiddifypanel/panel/admin/QuickSetup.py +1 -1
  35. hiddifypanel/panel/admin/SettingAdmin.py +7 -5
  36. hiddifypanel/panel/admin/Terminal.py +0 -1
  37. hiddifypanel/panel/admin/UserAdmin.py +4 -3
  38. hiddifypanel/panel/cli.py +36 -23
  39. hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +2 -4
  40. hiddifypanel/panel/commercial/restapi/v1/tgbot.py +7 -4
  41. hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +17 -13
  42. hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +4 -3
  43. hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +28 -10
  44. hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +2 -19
  45. hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +27 -4
  46. hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +28 -9
  47. hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -21
  48. hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +1 -1
  49. hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +8 -4
  50. hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +19 -3
  51. hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +48 -42
  52. hiddifypanel/panel/commercial/telegrambot/Usage.py +1 -1
  53. hiddifypanel/panel/commercial/telegrambot/admin.py +1 -1
  54. hiddifypanel/panel/commercial/telegrambot/information.py +1 -1
  55. hiddifypanel/panel/common.py +5 -11
  56. hiddifypanel/panel/hiddify.py +9 -20
  57. hiddifypanel/panel/init_db.py +31 -13
  58. hiddifypanel/panel/usage.py +38 -9
  59. hiddifypanel/panel/user/user.py +52 -32
  60. hiddifypanel/templates/admin-layout.html +2 -2
  61. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  62. hiddifypanel/translations/en/LC_MESSAGES/messages.po +80 -25
  63. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  64. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +74 -20
  65. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  66. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +60 -6
  67. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  68. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +158 -78
  69. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  70. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +60 -6
  71. hiddifypanel/translations.i18n/en.json +62 -22
  72. hiddifypanel/translations.i18n/fa.json +57 -17
  73. hiddifypanel/translations.i18n/pt.json +43 -3
  74. hiddifypanel/translations.i18n/ru.json +112 -72
  75. hiddifypanel/translations.i18n/zh.json +43 -3
  76. {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/METADATA +2 -1
  77. {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/RECORD +81 -81
  78. {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/WHEEL +1 -1
  79. hiddifypanel/panel/cf_api.py +0 -37
  80. {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/LICENSE.md +0 -0
  81. {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/entry_points.txt +0 -0
  82. {hiddifypanel-10.20.3.dist-info → hiddifypanel-10.30.0.dev0.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,6 @@ import json
3
3
 
4
4
  from hiddifypanel import hutils
5
5
  from hiddifypanel.models import ProxyProto, ProxyTransport, Domain, ConfigEnum
6
- from hiddifypanel.panel.hiddify import is_hiddify_next_version
7
6
 
8
7
 
9
8
  def configs_as_json(domains: list[Domain], **kwargs) -> str:
@@ -125,7 +124,7 @@ def add_multiplex(base: dict, proxy: dict):
125
124
  base['multiplex']['max_connections'] = proxy.get('mux_max_connections', 0)
126
125
  base['multiplex']['min_streams'] = proxy.get('mux_min_streams', 0)
127
126
 
128
- add_tcp_brutal(base,proxy)
127
+ add_tcp_brutal(base, proxy)
129
128
 
130
129
 
131
130
  def add_tcp_brutal(base: dict, proxy: dict):
@@ -252,7 +251,7 @@ def add_wireguard(base: dict, proxy: dict):
252
251
  base["pre_shared_key"] = proxy["wg_psk"]
253
252
 
254
253
  base["mtu"] = 1380
255
- if g.user_agent.get('is_hiddify') and is_hiddify_next_version(0, 15, 0):
254
+ if g.user_agent.get('is_hiddify') and hutils.flask.is_client_version(hutils.flask.ClientVersion.hiddify_next, 0, 15, 0):
256
255
  base["fake_packets"] = proxy["wg_noise_trick"]
257
256
 
258
257
 
@@ -1,7 +1,6 @@
1
1
  import datetime
2
2
  import json
3
- import copy
4
- from flask import render_template, request, g
3
+ from flask import request, g
5
4
  from hiddifypanel import hutils
6
5
  from hiddifypanel.models import ProxyTransport, ProxyL3, ProxyProto, Domain, User, ConfigEnum, hconfig
7
6
  from flask_babel import gettext as _
@@ -73,15 +72,18 @@ def to_link(proxy: dict) -> str | dict:
73
72
  baseurl = f'ssr://{proxy["cipher"]}:{proxy["uuid"]}@{proxy["server"]}:{proxy["port"]}'
74
73
  return baseurl
75
74
  if proxy['proto'] in ['ss', 'v2ray']:
76
- baseurl = f'ss://{proxy["cipher"]}:{proxy["password"]}@{proxy["server"]}:{proxy["port"]}'
77
- if proxy['mode'] == 'faketls':
78
- return f'{baseurl}?plugin=obfs-local%3Bobfs%3Dtls%3Bobfs-host%3D{proxy["fakedomain"]}%3Budp-over-tcp=true#{name_link}'
79
- # if proxy['mode'] == 'shadowtls':
80
- # return f'{baseurl}?plugin=shadow-tls%3Bpassword%3D{proxy["proxy_path"]}%3Bhost%3D{proxy["fakedomain"]}%3Budp-over-tcp=true#{name_link}'
81
- if proxy['proto'] == 'v2ray':
82
- return f'{baseurl}?plugin=v2ray-plugin%3Bmode%3Dwebsocket%3Bpath%3D{proxy["path"]}%3Bhost%3D{proxy["host"]}%3Btls%3Budp-over-tcp=true#{name_link}'
75
+ baseurl = f'ss://{hutils.encode.do_base_64(proxy["cipher"] + ":" + proxy["password"])}@{proxy["server"]}:{proxy["port"]}'
76
+
83
77
  if proxy['transport'] == 'shadowsocks':
84
- return baseurl
78
+ return f'{baseurl}#{name_link}'
79
+ if proxy['transport'] == 'faketls':
80
+ return f'{baseurl}?plugin=obfs-local&obfs-host={proxy["fakedomain"]}&obfs=http&udp-over-tcp=true#{name_link}'
81
+ if proxy['transport'] == 'shadowtls':
82
+ return "ShadowTLS is Not Supported for this platform"
83
+ # return f'{baseurl}?plugin=v2ray-plugin&path={proxy["proxy_path"]}&host={proxy["fakedomain"]}&udp-over-tcp=true#{name_link}'
84
+ if proxy['proto'] == 'v2ray':
85
+ return f'{baseurl}?plugin=v2ray-plugin&mode=websocket&path={proxy["proxy_path"]}&host={proxy["fakedomain"]}&tls&udp-over-tcp=true#{name_link}'
86
+
85
87
  if proxy['proto'] == 'tuic':
86
88
  baseurl = f'tuic://{proxy["uuid"]}:{proxy["uuid"]}@{proxy["server"]}:{proxy["port"]}?congestion_control=cubic&udp_relay_mode=native&sni={proxy["sni"]}&alpn=h3'
87
89
  if proxy['mode'] == 'Fake' or proxy['allow_insecure']:
@@ -5,14 +5,14 @@ from hiddifypanel import hutils
5
5
  from hiddifypanel.models import ProxyTransport, ProxyL3, ProxyProto, Domain, User
6
6
  from flask_babel import gettext as _
7
7
  from hiddifypanel.models import hconfig, ConfigEnum
8
- from .xray import is_muxable_agent
9
- OUTBOUND_LEVEL = 8
8
+ from .xray import is_muxable_agent, OUTBOUND_LEVEL
10
9
 
11
10
 
12
11
  def configs_as_json(domains: list[Domain], user: User, expire_days: int, remarks: str) -> str:
13
12
  '''Returns xray configs as json'''
14
-
15
13
  all_configs = []
14
+
15
+ # region show usage
16
16
  if hconfig(ConfigEnum.show_usage_in_sublink) and not g.user_agent.get('is_hiddify'):
17
17
  # determine usages
18
18
  tag = '⏳ ' if user.is_active else '✖ '
@@ -33,7 +33,9 @@ def configs_as_json(domains: list[Domain], user: User, expire_days: int, remark
33
33
  all_configs.append(
34
34
  null_config(tag)
35
35
  )
36
+ # endregion
36
37
 
38
+ # region show status (active/disable)
37
39
  active = True
38
40
  if not user.is_active:
39
41
  tag = '✖ ' + (hutils.encode.url_encode('بسته شما به پایان رسید') if hconfig(ConfigEnum.lang) == 'fa' else 'Package Ended')
@@ -42,13 +44,18 @@ def configs_as_json(domains: list[Domain], user: User, expire_days: int, remark
42
44
  null_config(tag)
43
45
  )
44
46
  active = False
47
+ # endregion
45
48
 
46
49
  if active:
50
+ # TODO: seperate codes to small functions
47
51
  # TODO: check what are unsupported protocols in other apps
48
- unsupported_protos = {}
52
+ unsupported_protos = []
53
+ unsupported_transport = []
49
54
  if g.user_agent.get('is_v2rayng'):
50
55
  # TODO: ensure which protocols are not supported in v2rayng
51
- unsupported_protos = {ProxyProto.wireguard, ProxyProto.hysteria, ProxyProto.hysteria2, ProxyProto.tuic, ProxyProto.ss, ProxyProto.ssr, ProxyProto.ssh}
56
+ unsupported_protos = [ProxyProto.wireguard, ProxyProto.hysteria, ProxyProto.hysteria2, ProxyProto.tuic, ProxyProto.ss, ProxyProto.ssr, ProxyProto.ssh]
57
+ if not hutils.flask.is_client_version(hutils.flask.ClientVersion.v2ryang, 1, 8, 18):
58
+ unsupported_transport = [ProxyTransport.httpupgrade]
52
59
 
53
60
  # multiple outbounds needs multiple whole base config not just one with multiple outbounds (at least for v2rayng)
54
61
  # https://github.com/2dust/v2rayNG/pull/2827#issue-2127534078
@@ -56,6 +63,8 @@ def configs_as_json(domains: list[Domain], user: User, expire_days: int, remark
56
63
  for proxy in hutils.proxy.get_valid_proxies(domains):
57
64
  if unsupported_protos and proxy['proto'] in unsupported_protos:
58
65
  continue
66
+ if unsupported_transport and proxy['transport'] in unsupported_transport:
67
+ continue
59
68
  outbound = to_xray(proxy)
60
69
  if 'msg' not in outbound:
61
70
  outbounds.append(outbound)
@@ -76,6 +85,7 @@ def configs_as_json(domains: list[Domain], user: User, expire_days: int, remark
76
85
 
77
86
  if not all_configs:
78
87
  return ''
88
+
79
89
  json_configs = json.dumps(all_configs, indent=2, cls=hutils.proxy.ProxyJsonEncoder)
80
90
  return json_configs
81
91
 
@@ -91,6 +101,7 @@ def to_xray(proxy: dict) -> dict:
91
101
  'concurrency': -1
92
102
  }
93
103
  }
104
+ outbound['protocol'] = 'shadowsocks' if outbound['protocol'] == 'ss' else outbound['protocol']
94
105
  # add multiplex to outbound
95
106
  add_multiplex(outbound, proxy)
96
107
 
@@ -211,7 +222,11 @@ def add_stream_settings(base: dict, proxy: dict):
211
222
  ss['security'] = 'tls'
212
223
 
213
224
  # network and transport settings
214
- if ss['security'] == 'tls' or 'xtls':
225
+ # THE CURRENT CODE WORKS BUT THE CORRECT CONDITINO SHOULD BE THIS:
226
+ # ss['security'] == 'tls' or 'xtls' -----> ss['security'] in ['tls','xtls']
227
+ # TODO: FIX THE CONDITION AND TEST CONFIGS ON THE CLIENT SIDE
228
+
229
+ if ss['security'] == 'tls' or 'xtls' and proxy['proto'] != ProxyProto.ss:
215
230
  ss['tlsSettings'] = {
216
231
  'serverName': proxy['sni'],
217
232
  'allowInsecure': proxy['allow_insecure'],
@@ -236,7 +251,7 @@ def add_stream_settings(base: dict, proxy: dict):
236
251
  if proxy['l3'] == ProxyL3.h3_quic:
237
252
  add_quic_stream(ss, proxy)
238
253
 
239
- if proxy['transport'] == 'tcp' or ss['security'] == 'reality' or (ss['security'] == 'none' and proxy['transport'] not in [ProxyTransport.httpupgrade, ProxyTransport.WS]):
254
+ 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):
240
255
  ss['network'] = proxy['transport']
241
256
  add_tcp_stream(ss, proxy)
242
257
  if proxy['transport'] == ProxyTransport.h2 and ss['security'] == 'none' and ss['security'] != 'reality':
@@ -252,6 +267,9 @@ def add_stream_settings(base: dict, proxy: dict):
252
267
  ss['network'] = proxy['transport']
253
268
  add_ws_stream(ss, proxy)
254
269
 
270
+ if proxy['proto'] == ProxyProto.ss:
271
+ ss['network'] = 'tcp'
272
+
255
273
  # tls fragmentaion
256
274
  add_tls_fragmentation_stream_settings(base, proxy)
257
275
 
@@ -379,7 +397,7 @@ def add_tls_fragmentation_stream_settings(base: dict, proxy: dict):
379
397
 
380
398
 
381
399
  def add_multiplex(base: dict, proxy: dict):
382
- if proxy.get('mux_enable') != "xray":
400
+ if proxy.get('mux_enable') != "xray" or not is_muxable_agent(proxy):
383
401
  return
384
402
 
385
403
  concurrency = proxy['mux_max_connections']
@@ -390,47 +408,6 @@ def add_multiplex(base: dict, proxy: dict):
390
408
  base['mux']['xudpProxyUDP443'] = 'reject'
391
409
 
392
410
 
393
- def add_tls_tricks_to_dict(d: dict, proxy: dict):
394
- if proxy.get('tls_fragment_enable'):
395
- if g.user_agent.get('is_shadowrocket'):
396
- d['fragment'] = f'1,{proxy["tls_fragment_size"]},{proxy["tls_fragment_sleep"]}'
397
- else:
398
- d['fragment'] = f'{proxy["tls_fragment_size"]},{proxy["tls_fragment_sleep"]},tlshello'
399
-
400
- if proxy.get("tls_mixed_case"):
401
- d['mc'] = 1
402
- if proxy.get("tls_padding_enable"):
403
- d['padsize'] = proxy["tls_padding_length"]
404
-
405
-
406
- def add_mux_to_dict(d: dict, proxy):
407
- if not is_muxable_agent(proxy):
408
- return
409
-
410
- # according to github.com/hiddify/ray2sing/
411
- d['muxtype'] = proxy["mux_protocol"]
412
- d['muxmaxc'] = proxy["mux_max_connections"]
413
- d['mux'] = proxy['mux_min_streams']
414
- d['muxsmax'] = proxy["mux_max_streams"]
415
- d['muxpad'] = proxy["mux_padding_enable"]
416
-
417
- if proxy.get('mux_brutal_enable'):
418
- d['muxup'] = proxy["mux_brutal_up_mbps"]
419
- d['muxdown'] = proxy["mux_brutal_down_mbps"]
420
-
421
-
422
- def add_tls_tricks_to_link(proxy: dict) -> str:
423
- out = {}
424
- add_tls_tricks_to_dict(out, proxy)
425
- return hutils.encode.convert_dict_to_url(out)
426
-
427
-
428
- def add_mux_to_link(proxy: dict) -> str:
429
- out = {}
430
- add_mux_to_dict(out, proxy)
431
- return hutils.encode.convert_dict_to_url(out)
432
-
433
-
434
411
  def null_config(tag: str) -> dict:
435
412
  base_config = json.loads(render_template('base_xray_config.json.j2', remarks=tag))
436
413
  return base_config
@@ -1,9 +1,11 @@
1
1
  from flask_babel import lazy_gettext as _
2
2
  import requests
3
-
3
+ from packaging.version import Version
4
4
  import re
5
5
  import sys
6
6
 
7
+ from hiddifypanel.models.config import hconfig, ConfigEnum
8
+ from hiddifypanel import __version__ as current_version
7
9
  from hiddifypanel.cache import cache
8
10
 
9
11
 
@@ -35,7 +37,49 @@ def get_latest_release_version(repo_name):
35
37
  if ver == "latest":
36
38
  return get_latest_release_version(repo_name.replace("-", ""))
37
39
  return ver
38
- except Exception as e:
39
- return f'{e}'
40
+ except Exception:
41
+ return None
40
42
 
41
43
  return None
44
+
45
+
46
+ def is_panel_outdated() -> bool:
47
+ # TODO: handle beta and develop version too
48
+ pm = hconfig(ConfigEnum.package_mode)
49
+ try:
50
+ if pm == 'release':
51
+ if latest_v := get_latest_release_version('hiddifypanel'):
52
+ if compare_versions(latest_v, current_version) == 1:
53
+ return True
54
+ except:
55
+ pass
56
+ return False
57
+
58
+
59
+ def compare_versions(version_1: str, version_2: str) -> int:
60
+ """
61
+ Compare two version strings and return an integer based on their relative order.
62
+ Returns:
63
+ int:
64
+
65
+ - 1 if version_1 is greater than version_2.
66
+ - 0 if version_1 is equal to version_2.
67
+ - -1 if version_1 is less than version_2.
68
+
69
+ Examples:
70
+ >>> compare_versions("10.20.4", "10.20.4")
71
+ 0
72
+ >>> compare_versions("10.20.4", "10.20.2")
73
+ 1
74
+ >>> compare_versions("10.20.2", "10.20.4")
75
+ -1
76
+ """
77
+ v1 = Version(version_1)
78
+ v2 = Version(version_2)
79
+
80
+ if v1 > v2:
81
+ return 1 # version_1 is greater
82
+ elif v2 > v1:
83
+ return -1 # version_2 is greater
84
+ else:
85
+ return 0 # versions are equal
@@ -4,7 +4,7 @@ from .config_enum import ConfigCategory, ConfigEnum, Lang, ApplyMode, PanelMode,
4
4
  from .config import StrConfig, BoolConfig, get_hconfigs, hconfig, set_hconfig, add_or_update_config, bulk_register_configs, get_hconfigs_childs
5
5
 
6
6
  # from .parent_domain import ParentDomain
7
- from .domain import Domain, DomainType, ShowDomain, get_domain, get_current_proxy_domains, get_panel_domains, get_proxy_domains, get_proxy_domains_db, get_hdomains, hdomain, add_or_update_domain, bulk_register_domains, get_panel_link
7
+ from .domain import Domain, DomainType, ShowDomain
8
8
  from .proxy import Proxy, ProxyL3, ProxyCDN, ProxyProto, ProxyTransport
9
9
  from .user import User, UserMode, UserDetail, ONE_GIG
10
10
  from .admin import AdminUser, AdminMode
@@ -67,16 +67,21 @@ class AdminUser(BaseAccount, SerializerMixin):
67
67
  base = super().to_dict()
68
68
  if dump_id:
69
69
  base['id'] = self.id
70
- from hiddifypanel.models import hconfig, ConfigEnum
70
+ if not base.get('lang'):
71
+ from hiddifypanel.models import hconfig, ConfigEnum
72
+ base['lang'] = hconfig(ConfigEnum.admin_lang)
71
73
  return {**base,
72
74
  'mode': self.mode,
73
75
  'can_add_admin': self.can_add_admin,
74
76
  'parent_admin_uuid': self.parent_admin.uuid if self.parent_admin else None,
75
- 'lang': hconfig(ConfigEnum.admin_lang)
77
+ 'max_users': self.max_users,
78
+ 'max_active_users': self.max_active_users,
76
79
  }
77
80
 
78
81
  @classmethod
79
82
  def by_uuid(cls, uuid: str, create: bool = False) -> BaseAccount | None:
83
+ if not isinstance(uuid, str):
84
+ uuid = str(uuid)
80
85
  account = AdminUser.query.filter(AdminUser.uuid == uuid).first()
81
86
  if not account and create:
82
87
  dbuser = AdminUser(uuid=uuid, name="unknown", parent_admin_id=AdminUser.current_admin_or_owner().id)
@@ -101,6 +106,8 @@ class AdminUser(BaseAccount, SerializerMixin):
101
106
 
102
107
  dbuser.mode = data.get('mode', AdminMode.agent)
103
108
  dbuser.can_add_admin = data.get('can_add_admin') or False
109
+ dbuser.max_users = data.get('max_users', 100)
110
+ dbuser.max_active_users = data.get('max_active_users', 100)
104
111
  if commit:
105
112
  db.session.commit()
106
113
  return dbuser
@@ -49,6 +49,8 @@ class BaseAccount(db.Model, SerializerMixin, FlaskLoginUserMixin): # type: igno
49
49
 
50
50
  @classmethod
51
51
  def by_uuid(cls, uuid: str, create: bool = False):
52
+ if not isinstance(uuid, str):
53
+ uuid = str(uuid)
52
54
  account = cls.query.filter(cls.uuid == uuid).first()
53
55
  if not account and create:
54
56
  raise NotImplementedError
@@ -60,10 +62,10 @@ class BaseAccount(db.Model, SerializerMixin, FlaskLoginUserMixin): # type: igno
60
62
 
61
63
  @classmethod
62
64
  def add_or_update(cls, commit: bool = True, **data):
63
- from hiddifypanel import hutils
64
65
  db_account = cls.by_uuid(data['uuid'], create=True)
65
66
  db_account.name = data.get('name', '')
66
67
  db_account.comment = data.get('comment', '')
68
+ from hiddifypanel import hutils
67
69
  db_account.telegram_id = hutils.convert.to_int(data.get('telegram_id'))
68
70
  db_account.lang = data.get('lang')
69
71
  if commit:
@@ -45,11 +45,11 @@ class StrConfig(db.Model, SerializerMixin):
45
45
  key = Column(Enum(ConfigEnum), primary_key=True, default=ConfigEnum.admin_secret)
46
46
  value = Column(String(2048))
47
47
 
48
- def to_dict(d):
48
+ def to_dict(self: "StrConfig"):
49
49
  return {
50
- 'key': str(d.key),
51
- 'value': d.value,
52
- 'child_unique_id': d.child.unique_id if d.child else ''
50
+ 'key': str(self.key),
51
+ 'value': self.value,
52
+ 'child_unique_id': self.child.unique_id if self.child else ''
53
53
  }
54
54
 
55
55
  @staticmethod
@@ -119,6 +119,7 @@ def set_hconfig(key: ConfigEnum, value: str | int | bool, child_id: int | None =
119
119
  else:
120
120
  old_v = dbconf.value
121
121
  else:
122
+ value = str(value)
122
123
  dbconf = StrConfig.query.filter(StrConfig.key == key, StrConfig.child_id == child_id).first()
123
124
  if not dbconf:
124
125
  dbconf = StrConfig(key=key, value=value, child_id=child_id)
@@ -144,9 +145,6 @@ def get_hconfigs(child_id: int | None = None, json=False) -> dict:
144
145
 
145
146
  return {**{f'{u.key}' if json else u.key: u.value for u in BoolConfig.query.filter(BoolConfig.child_id == child_id).all() if u.key.type == bool},
146
147
  **{f'{u.key}' if json else u.key: int(u.value) if u.key.type == int and u.value != None else u.value for u in StrConfig.query.filter(StrConfig.child_id == child_id).all() if u.key.type != bool},
147
- # ConfigEnum.telegram_fakedomain:hdomain(DomainType.telegram_faketls),
148
- # ConfigEnum.ssfaketls_fakedomain:hdomain(DomainType.ss_faketls),
149
- # ConfigEnum.fake_cdn_domain:hdomain(DomainType.fake_cdn)
150
148
  }
151
149
 
152
150
 
@@ -69,19 +69,19 @@ class ApplyMode(StrEnum):
69
69
  nothing = auto()
70
70
 
71
71
 
72
- def _BoolConfigDscr(category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False):
72
+ def _BoolConfigDscr(category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False) -> "ConfigEnum":
73
73
  return category, apply_mode, bool, show_in_parent
74
74
 
75
75
 
76
- def _StrConfigDscr(category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False):
76
+ def _StrConfigDscr(category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False) -> "ConfigEnum":
77
77
  return category, apply_mode, str, show_in_parent
78
78
 
79
79
 
80
- def _IntConfigDscr(category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False):
80
+ def _IntConfigDscr(category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False) -> "ConfigEnum":
81
81
  return category, apply_mode, int, show_in_parent
82
82
 
83
83
 
84
- def _TypedConfigDscr(ctype: type, category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False):
84
+ def _TypedConfigDscr(ctype: type, category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False) -> "ConfigEnum":
85
85
  return category, apply_mode, ctype, show_in_parent
86
86
 
87
87
 
@@ -116,7 +116,7 @@ class ConfigEnum(metaclass=FastEnum):
116
116
  first_setup = _BoolConfigDscr(ConfigCategory.hidden)
117
117
  core_type = _StrConfigDscr(ConfigCategory.advanced, ApplyMode.apply, hide_in_virtual_child=True)
118
118
  warp_enable = _BoolConfigDscr(ConfigCategory.hidden, ApplyMode.restart, hide_in_virtual_child=True)
119
- warp_mode = _StrConfigDscr(ConfigCategory.warp, ApplyMode.apply, hide_in_virtual_child=True)
119
+ warp_mode = _StrConfigDscr(ConfigCategory.warp, ApplyMode.restart, hide_in_virtual_child=True)
120
120
  warp_plus_code = _StrConfigDscr(ConfigCategory.warp, ApplyMode.apply, hide_in_virtual_child=True)
121
121
  warp_sites = _StrConfigDscr(ConfigCategory.warp, ApplyMode.apply, hide_in_virtual_child=True)
122
122
  dns_server = _StrConfigDscr(ConfigCategory.general, ApplyMode.apply, hide_in_virtual_child=True)
@@ -222,7 +222,7 @@ class ConfigEnum(metaclass=FastEnum):
222
222
  shadowsocks2022_enable = _BoolConfigDscr(ConfigCategory.shadowsocks, ApplyMode.apply)
223
223
  shadowsocks2022_method = _StrConfigDscr(ConfigCategory.hidden, ApplyMode.apply)
224
224
  shadowsocks2022_port = _StrConfigDscr(ConfigCategory.shadowsocks, ApplyMode.apply)
225
- ssfaketls_enable = _BoolConfigDscr(ConfigCategory.shadowsocks, ApplyMode.apply)
225
+ ssfaketls_enable = _BoolConfigDscr(ConfigCategory.shadowsocks, ApplyMode.restart)
226
226
  ssfaketls_fakedomain = _StrConfigDscr(ConfigCategory.shadowsocks, ApplyMode.apply, hide_in_virtual_child=True)
227
227
  shadowtls_enable = _BoolConfigDscr(ConfigCategory.shadowsocks, ApplyMode.apply)
228
228
  shadowtls_fakedomain = _StrConfigDscr(ConfigCategory.shadowsocks, ApplyMode.apply, hide_in_virtual_child=True)
@@ -248,6 +248,7 @@ class ConfigEnum(metaclass=FastEnum):
248
248
  h2_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
249
249
 
250
250
  db_version = _StrConfigDscr(ConfigCategory.hidden, ApplyMode.apply)
251
+ last_priodic_usage_check = _IntConfigDscr(ConfigCategory.hidden, ApplyMode.apply)
251
252
 
252
253
  branding_title = _StrConfigDscr(ConfigCategory.branding)
253
254
  branding_site = _StrConfigDscr(ConfigCategory.branding)
@@ -264,6 +265,17 @@ class ConfigEnum(metaclass=FastEnum):
264
265
  path_tcp = _StrConfigDscr(ConfigCategory.too_advanced, hide_in_virtual_child=True)
265
266
  path_grpc = _StrConfigDscr(ConfigCategory.too_advanced, hide_in_virtual_child=True)
266
267
 
268
+ # subs
269
+ sub_full_singbox_enable = _BoolConfigDscr(ConfigCategory.hidden)
270
+ sub_singbox_ssh_enable = _BoolConfigDscr(ConfigCategory.hidden)
271
+ sub_full_xray_json_enable = _BoolConfigDscr(ConfigCategory.proxies)
272
+ sub_full_links_enable = _BoolConfigDscr(ConfigCategory.hidden)
273
+ sub_full_links_b64_enable = _BoolConfigDscr(ConfigCategory.hidden)
274
+ sub_full_clash_enable = _BoolConfigDscr(ConfigCategory.hidden)
275
+ sub_full_clash_meta_enable = _BoolConfigDscr(ConfigCategory.hidden)
276
+
277
+ hiddifycli_enable = _BoolConfigDscr(ConfigCategory.hidden, ApplyMode.restart)
278
+
267
279
  @classmethod
268
280
  def __missing__(cls, value):
269
281
  return ConfigEnum.not_found