hiddifypanel 10.12.1__py3-none-any.whl → 10.15.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 (117) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/auth.py +15 -4
  4. hiddifypanel/base.py +58 -50
  5. hiddifypanel/cache.py +43 -25
  6. hiddifypanel/database.py +9 -0
  7. hiddifypanel/drivers/abstract_driver.py +2 -0
  8. hiddifypanel/drivers/singbox_api.py +17 -15
  9. hiddifypanel/drivers/ssh_liberty_bridge_api.py +2 -0
  10. hiddifypanel/drivers/user_driver.py +12 -6
  11. hiddifypanel/drivers/wireguard_api.py +2 -0
  12. hiddifypanel/drivers/xray_api.py +14 -9
  13. hiddifypanel/hutils/__init__.py +1 -0
  14. hiddifypanel/hutils/convert.py +13 -2
  15. hiddifypanel/hutils/crypto.py +21 -2
  16. hiddifypanel/hutils/flask.py +19 -5
  17. hiddifypanel/hutils/importer/xui.py +5 -2
  18. hiddifypanel/hutils/node/__init__.py +3 -0
  19. hiddifypanel/hutils/node/api_client.py +76 -0
  20. hiddifypanel/hutils/node/child.py +147 -0
  21. hiddifypanel/hutils/node/parent.py +100 -0
  22. hiddifypanel/hutils/node/shared.py +65 -0
  23. hiddifypanel/hutils/proxy/shared.py +15 -3
  24. hiddifypanel/models/__init__.py +2 -2
  25. hiddifypanel/models/admin.py +14 -2
  26. hiddifypanel/models/base_account.py +3 -3
  27. hiddifypanel/models/child.py +30 -16
  28. hiddifypanel/models/config.py +39 -15
  29. hiddifypanel/models/config_enum.py +55 -8
  30. hiddifypanel/models/domain.py +28 -20
  31. hiddifypanel/models/parent_domain.py +2 -2
  32. hiddifypanel/models/proxy.py +13 -4
  33. hiddifypanel/models/report.py +2 -3
  34. hiddifypanel/models/usage.py +2 -2
  35. hiddifypanel/models/user.py +18 -9
  36. hiddifypanel/panel/admin/Actions.py +4 -6
  37. hiddifypanel/panel/admin/AdminstratorAdmin.py +13 -2
  38. hiddifypanel/panel/admin/Dashboard.py +5 -10
  39. hiddifypanel/panel/admin/DomainAdmin.py +12 -11
  40. hiddifypanel/panel/admin/NodeAdmin.py +6 -2
  41. hiddifypanel/panel/admin/ProxyAdmin.py +4 -3
  42. hiddifypanel/panel/admin/SettingAdmin.py +60 -21
  43. hiddifypanel/panel/admin/UserAdmin.py +10 -2
  44. hiddifypanel/panel/admin/templates/index.html +1 -1
  45. hiddifypanel/panel/admin/templates/parent_dash.html +2 -4
  46. hiddifypanel/panel/cli.py +16 -16
  47. hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +10 -5
  48. hiddifypanel/panel/commercial/__init__.py +7 -5
  49. hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +0 -5
  50. hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +2 -2
  51. hiddifypanel/panel/commercial/restapi/v2/admin/admin_log_api.py +4 -5
  52. hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +8 -35
  53. hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +4 -4
  54. hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +157 -0
  55. hiddifypanel/panel/commercial/restapi/v2/admin/server_status_api.py +3 -3
  56. hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +9 -73
  57. hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -1
  58. hiddifypanel/panel/commercial/restapi/v2/child/__init__.py +18 -0
  59. hiddifypanel/panel/commercial/restapi/v2/child/actions.py +63 -0
  60. hiddifypanel/panel/commercial/restapi/v2/child/register_parent_api.py +34 -0
  61. hiddifypanel/panel/commercial/restapi/v2/child/schema.py +7 -0
  62. hiddifypanel/panel/commercial/restapi/v2/child/sync_parent_api.py +21 -0
  63. hiddifypanel/panel/commercial/restapi/v2/panel/__init__.py +13 -0
  64. hiddifypanel/panel/commercial/restapi/v2/panel/info.py +18 -0
  65. hiddifypanel/panel/commercial/restapi/v2/panel/ping_pong.py +23 -0
  66. hiddifypanel/panel/commercial/restapi/v2/panel/schema.py +7 -0
  67. hiddifypanel/panel/commercial/restapi/v2/parent/__init__.py +16 -0
  68. hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +65 -0
  69. hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +115 -0
  70. hiddifypanel/panel/commercial/restapi/v2/parent/status_api.py +26 -0
  71. hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +53 -0
  72. hiddifypanel/panel/commercial/restapi/v2/parent/usage_api.py +57 -0
  73. hiddifypanel/panel/commercial/telegrambot/admin.py +1 -2
  74. hiddifypanel/panel/common.py +21 -6
  75. hiddifypanel/panel/hiddify.py +9 -80
  76. hiddifypanel/panel/init_db.py +84 -38
  77. hiddifypanel/panel/usage.py +33 -18
  78. hiddifypanel/panel/user/templates/home/usage.html +1 -1
  79. hiddifypanel/panel/user/templates/new.html +2 -2
  80. hiddifypanel/static/css/custom.css +13 -0
  81. hiddifypanel/static/images/hiddify.png +0 -0
  82. hiddifypanel/static/images/hiddify2.png +0 -0
  83. hiddifypanel/static/new/assets/hiddify-logo-7617d937.png +0 -0
  84. hiddifypanel/static/new/assets/{index-4510b616.js → index-ccb9873c.js} +2 -2
  85. hiddifypanel/static/new/assets/index-fa00de9a.css +1 -0
  86. hiddifypanel/static/new/i18n/en.json +6 -6
  87. hiddifypanel/static/new/i18n/fa.json +1 -1
  88. hiddifypanel/templates/admin-layout.html +24 -40
  89. hiddifypanel/templates/fake.html +22 -0
  90. hiddifypanel/templates/master.html +24 -42
  91. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  92. hiddifypanel/translations/en/LC_MESSAGES/messages.po +95 -5
  93. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  94. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +96 -4
  95. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  96. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +98 -6
  97. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  98. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +91 -1
  99. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  100. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +92 -2
  101. hiddifypanel/translations.i18n/en.json +61 -5
  102. hiddifypanel/translations.i18n/fa.json +60 -4
  103. hiddifypanel/translations.i18n/pt.json +63 -7
  104. hiddifypanel/translations.i18n/ru.json +57 -1
  105. hiddifypanel/translations.i18n/zh.json +58 -2
  106. {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/METADATA +47 -47
  107. {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/RECORD +112 -94
  108. hiddifypanel/panel/commercial/restapi/v2/DTO.py +0 -9
  109. hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py +0 -16
  110. hiddifypanel/panel/commercial/restapi/v2/hello/hello.py +0 -32
  111. hiddifypanel/static/new/assets/hiddify-logo-7617d937_old.png +0 -0
  112. hiddifypanel/static/new/assets/index-669b32c8.css +0 -1
  113. /hiddifypanel/static/images/{hiddify1.png → hiddify-old.png} +0 -0
  114. {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/LICENSE.md +0 -0
  115. {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/WHEEL +0 -0
  116. {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/entry_points.txt +0 -0
  117. {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,13 @@
1
- from hiddifypanel.models.config_enum import ConfigEnum
1
+ from typing import Optional
2
+ from hiddifypanel.models.config_enum import ConfigEnum, LogLevel, PanelMode, Lang
2
3
  from flask import g
3
- from sqlalchemy_serializer import SerializerMixin
4
+
4
5
  from hiddifypanel import Events
5
6
  from hiddifypanel.database import db
6
7
  from hiddifypanel.cache import cache
7
8
  from hiddifypanel.models.child import Child, ChildMode
8
9
  from sqlalchemy import Column, String, Boolean, Enum, ForeignKey, Integer
10
+ from strenum import StrEnum
9
11
 
10
12
 
11
13
  def error(st):
@@ -13,7 +15,7 @@ def error(st):
13
15
  err(st)
14
16
 
15
17
 
16
- class BoolConfig(db.Model, SerializerMixin):
18
+ class BoolConfig(db.Model):
17
19
  child_id = Column(Integer, ForeignKey('child.id'), primary_key=True, default=0)
18
20
  # category = db.Column(db.String(128), primary_key=True)
19
21
  key = Column(Enum(ConfigEnum), primary_key=True)
@@ -26,8 +28,17 @@ class BoolConfig(db.Model, SerializerMixin):
26
28
  'child_unique_id': d.child.unique_id if d.child else ''
27
29
  }
28
30
 
31
+ @staticmethod
32
+ def from_schema(schema):
33
+ return schema.dump(BoolConfig())
34
+
35
+ def to_schema(self):
36
+ conf_dict = self.to_dict()
37
+ from hiddifypanel.panel.commercial.restapi.v2.parent.schema import HConfigSchema
38
+ return HConfigSchema().load(conf_dict)
29
39
 
30
- class StrConfig(db.Model, SerializerMixin):
40
+
41
+ class StrConfig(db.Model):
31
42
  child_id = Column(Integer, ForeignKey('child.id'), primary_key=True, default=0)
32
43
  # category = db.Column(db.String(128), primary_key=True)
33
44
  key = Column(Enum(ConfigEnum), primary_key=True, default=ConfigEnum.admin_secret)
@@ -40,11 +51,20 @@ class StrConfig(db.Model, SerializerMixin):
40
51
  'child_unique_id': d.child.unique_id if d.child else ''
41
52
  }
42
53
 
54
+ @staticmethod
55
+ def from_schema(schema):
56
+ return schema.dump(StrConfig())
57
+
58
+ def to_schema(self):
59
+ conf_dict = self.to_dict()
60
+ from hiddifypanel.panel.commercial.restapi.v2.parent.schema import HConfigSchema
61
+ return HConfigSchema().load(conf_dict)
62
+
43
63
 
44
64
  @cache.cache(ttl=500)
45
- def hconfig(key: ConfigEnum, child_id: int | None = None) -> str | int | None:
65
+ def hconfig(key: ConfigEnum, child_id: Optional[int] = None): # -> str | int | StrEnum | None:
46
66
  if child_id is None:
47
- child_id = Child.current.id
67
+ child_id = Child.current().id
48
68
 
49
69
  value = None
50
70
  try:
@@ -63,14 +83,18 @@ def hconfig(key: ConfigEnum, child_id: int | None = None) -> str | int | None:
63
83
  except BaseException:
64
84
  error(f'{key} error!')
65
85
  raise
66
- if key.type == int and value != None:
67
- return int(value)
86
+ if value != None:
87
+ if key.type == int:
88
+ return int(value)
89
+ elif hasattr(key.type, 'from_str'):
90
+ return key.type.from_str(value)
91
+
68
92
  return value
69
93
 
70
94
 
71
95
  def set_hconfig(key: ConfigEnum, value: str | int | bool, child_id: int | None = None, commit: bool = True):
72
96
  if child_id is None:
73
- child_id = Child.current.id
97
+ child_id = Child.current().id
74
98
 
75
99
  print(f"chainging .... {key}---{value}---{child_id}---{commit}")
76
100
  if key.type == int and value != None:
@@ -81,7 +105,7 @@ def set_hconfig(key: ConfigEnum, value: str | int | bool, child_id: int | None =
81
105
  hconfig.invalidate(key, child_id)
82
106
  hconfig.invalidate(key, child_id=child_id)
83
107
  hconfig.invalidate(key=key, child_id=child_id)
84
- if child_id == Child.current.id:
108
+ if child_id == Child.current().id:
85
109
  hconfig.invalidate(key)
86
110
  # hconfig.invalidate_all()
87
111
  get_hconfigs.invalidate_all()
@@ -115,7 +139,7 @@ def set_hconfig(key: ConfigEnum, value: str | int | bool, child_id: int | None =
115
139
  @cache.cache(ttl=500,)
116
140
  def get_hconfigs(child_id: int | None = None, json=False) -> dict:
117
141
  if child_id is None:
118
- child_id = Child.current.id
142
+ child_id = Child.current().id
119
143
 
120
144
  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},
121
145
  **{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},
@@ -131,9 +155,9 @@ def get_hconfigs_childs(child_ids: list[int], json=False):
131
155
  return {c: get_hconfigs(c, json) for c in child_ids}
132
156
 
133
157
 
134
- def add_or_update_config(commit: bool = True, child_id: int = None, override_unique_id: bool = True, **config):
158
+ def add_or_update_config(commit: bool = True, child_id: int | None = None, override_unique_id: bool = True, **config):
135
159
  if child_id is None:
136
- child_id = Child.current.id
160
+ child_id = Child.current().id
137
161
  c = config['key']
138
162
  ckey = ConfigEnum(c)
139
163
  if c == ConfigEnum.unique_id and not override_unique_id:
@@ -145,13 +169,13 @@ def add_or_update_config(commit: bool = True, child_id: int = None, override_uni
145
169
  set_hconfig(ckey, v, child_id, commit=commit)
146
170
 
147
171
 
148
- def bulk_register_configs(hconfigs, commit: bool = True, override_child_unique_id: int | None = None, override_unique_id: bool = True):
172
+ def bulk_register_configs(hconfigs, commit: bool = True, froce_child_unique_id: str | None = None, override_unique_id: bool = True):
149
173
  from hiddifypanel.panel import hiddify
150
174
  for conf in hconfigs:
151
175
  # print(conf)
152
176
  if conf['key'] == ConfigEnum.unique_id and not override_unique_id:
153
177
  continue
154
- child_id = hiddify.get_child(unique_id=None)
178
+ child_id = hiddify.get_child(unique_id=froce_child_unique_id)
155
179
  # print(conf, child_id, conf.get('child_unique_id', None), override_child_unique_id)
156
180
  add_or_update_config(commit=False, child_id=child_id, **conf)
157
181
  if commit:
@@ -5,7 +5,13 @@ from strenum import StrEnum
5
5
  from fast_enum import FastEnum
6
6
 
7
7
 
8
- class Lang(StrEnum):
8
+ class HEnum(StrEnum):
9
+ @classmethod
10
+ def from_str(cls, key: str) -> 'HEnum':
11
+ return cls[key]
12
+
13
+
14
+ class Lang(HEnum):
9
15
  en = auto()
10
16
  fa = auto()
11
17
  ru = auto()
@@ -13,6 +19,22 @@ class Lang(StrEnum):
13
19
  zh = auto()
14
20
 
15
21
 
22
+ class PanelMode(HEnum):
23
+ standalone = auto()
24
+ parent = auto()
25
+ child = auto()
26
+
27
+
28
+ class LogLevel(HEnum):
29
+ TRACE = auto()
30
+ DEBUG = auto()
31
+ INFO = auto()
32
+ SUCCESS = auto()
33
+ WARNING = auto()
34
+ ERROR = auto()
35
+ CRITICAL = auto()
36
+
37
+
16
38
  class ConfigCategory(StrEnum):
17
39
  admin = auto()
18
40
  branding = auto()
@@ -59,11 +81,15 @@ def _IntConfigDscr(category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.n
59
81
  return category, apply_mode, int, show_in_parent
60
82
 
61
83
 
84
+ def _TypedConfigDscr(ctype: type, category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.nothing, show_in_parent: bool = True, hide_in_virtual_child=False):
85
+ return category, apply_mode, ctype, show_in_parent
86
+
87
+
62
88
  class ConfigEnum(metaclass=FastEnum):
63
89
  # category: ConfigCategory
64
- __slots__ = ('category', 'apply_mode', 'type', 'show_in_parent', 'hide_in_virtual_child')
90
+ __slots__ = ('name', 'value', 'category', 'apply_mode', 'type', 'show_in_parent', 'hide_in_virtual_child')
65
91
 
66
- def __init__(self, category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.apply, ctype=str, show_in_parent: bool = True, hide_in_virtual_child=False, name=auto):
92
+ def __init__(self, category: ConfigCategory, apply_mode: ApplyMode = ApplyMode.apply, ctype=type, show_in_parent: bool = True, hide_in_virtual_child=False, name=auto):
67
93
  self.value = name
68
94
  self.name = name
69
95
  self.category = category
@@ -110,13 +136,27 @@ class ConfigEnum(metaclass=FastEnum):
110
136
  package_mode = _StrConfigDscr(ConfigCategory.advanced, hide_in_virtual_child=True)
111
137
  utls = _StrConfigDscr(ConfigCategory.advanced)
112
138
  telegram_bot_token = _StrConfigDscr(ConfigCategory.telegram, ApplyMode.apply, hide_in_virtual_child=True)
139
+
140
+ # region child-parent
141
+ # deprecated
113
142
  is_parent = _BoolConfigDscr(ConfigCategory.hidden)
114
- parent_panel = _StrConfigDscr(ConfigCategory.hidden)
143
+ # parent panel domain
144
+ parent_panel = _StrConfigDscr(ConfigCategory.hidden) # should be able to change by user
145
+ parent_domain = _StrConfigDscr(ConfigCategory.hidden)
146
+ parent_admin_proxy_path = _StrConfigDscr(ConfigCategory.hidden)
147
+
148
+ # the panel mode could be one of these: "parent", "child", "standalone"
149
+ # this config value would be 'standalone' by default. and would be set by panel itself
150
+ panel_mode = _TypedConfigDscr(PanelMode, ConfigCategory.hidden, ApplyMode.nothing, hide_in_virtual_child=True)
151
+ # endregion
152
+
153
+ log_level = _TypedConfigDscr(LogLevel, ConfigCategory.hidden, ApplyMode.restart, hide_in_virtual_child=True)
154
+
115
155
  unique_id = _StrConfigDscr(ConfigCategory.hidden)
116
156
  last_hash = _StrConfigDscr(ConfigCategory.hidden)
117
157
  cdn_forced_host = _StrConfigDscr(ConfigCategory.hidden) # removed
118
- lang = _StrConfigDscr(ConfigCategory.branding)
119
- admin_lang = _StrConfigDscr(ConfigCategory.admin)
158
+ lang = _TypedConfigDscr(Lang, ConfigCategory.branding)
159
+ admin_lang = _TypedConfigDscr(Lang, ConfigCategory.admin)
120
160
  admin_secret = _StrConfigDscr(ConfigCategory.hidden) # removed
121
161
 
122
162
  # tls
@@ -199,6 +239,13 @@ class ConfigEnum(metaclass=FastEnum):
199
239
  ws_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
200
240
  grpc_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
201
241
  httpupgrade_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
242
+ vless_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
243
+ trojan_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
244
+ reality_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
245
+ tcp_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
246
+ quic_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
247
+ xtls_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
248
+ h2_enable = _BoolConfigDscr(ConfigCategory.proxies, ApplyMode.apply)
202
249
 
203
250
  db_version = _StrConfigDscr(ConfigCategory.hidden, ApplyMode.apply)
204
251
 
@@ -234,7 +281,7 @@ class ConfigEnum(metaclass=FastEnum):
234
281
  return not self.__eq__(other)
235
282
 
236
283
  def endswith(self, other):
237
- return self.name.endswith(other)
284
+ return self.name.endswith(other) # type: ignore
238
285
 
239
286
  def startswith(self, other):
240
- return self.name.startswith(other)
287
+ return self.name.startswith(other) # type: ignore
@@ -4,7 +4,7 @@ from typing import List
4
4
  from urllib.parse import urlparse
5
5
 
6
6
  from flask import request
7
- from sqlalchemy_serializer import SerializerMixin
7
+
8
8
  from flask_babel import lazy_gettext as _
9
9
  from sqlalchemy.orm import backref
10
10
  from urllib.parse import urlparse
@@ -40,7 +40,7 @@ ShowDomain = db.Table('show_domain',
40
40
  )
41
41
 
42
42
 
43
- class Domain(db.Model, SerializerMixin):
43
+ class Domain(db.Model):
44
44
  id = db.Column(db.Integer, primary_key=True, autoincrement=True)
45
45
  child_id = db.Column(db.Integer, db.ForeignKey('child.id'), default=0)
46
46
  domain = db.Column(db.String(200), nullable=False, unique=False)
@@ -67,7 +67,7 @@ class Domain(db.Model, SerializerMixin):
67
67
  'domain': self.domain.lower(),
68
68
  'mode': self.mode,
69
69
  'alias': self.alias,
70
- # 'sub_link_only':d.sub_link_only,
70
+ 'sub_link_only': self.sub_link_only,
71
71
  'child_unique_id': self.child.unique_id if self.child else '',
72
72
  'cdn_ip': self.cdn_ip,
73
73
  'servernames': self.servernames,
@@ -84,6 +84,15 @@ class Domain(db.Model, SerializerMixin):
84
84
 
85
85
  return data
86
86
 
87
+ @staticmethod
88
+ def from_schema(schema):
89
+ return schema.dump(Domain())
90
+
91
+ def to_schema(self):
92
+ domain_dict = self.to_dict()
93
+ from hiddifypanel.panel.commercial.restapi.v2.parent.schema import DomainSchema
94
+ return DomainSchema().load(domain_dict)
95
+
87
96
  @property
88
97
  def need_valid_ssl(self):
89
98
  return self.mode in [DomainType.direct, DomainType.cdn, DomainType.worker, DomainType.relay, DomainType.auto_cdn_ip, DomainType.old_xtls_direct, DomainType.sub_link_only]
@@ -137,14 +146,22 @@ def get_domain(domain):
137
146
  return Domain.query.filter(Domain.domain == domain).first()
138
147
 
139
148
 
149
+ def get_panel_link(child_id: int | None = None) -> Domain | None:
150
+ if child_id is None:
151
+ child_id = Child.current().id
152
+ domains = Domain.query.filter(Domain.mode.in_(
153
+ [DomainType.direct, DomainType.cdn, DomainType.worker, DomainType.relay, DomainType.auto_cdn_ip, DomainType.old_xtls_direct, DomainType.sub_link_only]),
154
+ Domain.child_id == child_id
155
+ ).all()
156
+ if not domains:
157
+ return None
158
+ return domains[0]
159
+
160
+
140
161
  def get_panel_domains(always_add_ip=False, always_add_all_domains=False) -> List[Domain]:
141
162
  from hiddifypanel import hutils
142
163
  domains = []
143
- # if hconfig(ConfigEnum.is_parent):
144
- # from .parent_domain import ParentDomain
145
- # domains = ParentDomain.query.all()
146
- # else:
147
- domains = Domain.query.filter(Domain.mode == DomainType.sub_link_only, Domain.child_id == Child.current.id).all()
164
+ domains = Domain.query.filter(Domain.mode == DomainType.sub_link_only, Domain.child_id == Child.current().id).all()
148
165
  if not len(domains) or always_add_all_domains:
149
166
  domains = Domain.query.filter(Domain.mode.notin_([DomainType.fake, DomainType.reality])).all()
150
167
 
@@ -156,11 +173,7 @@ def get_panel_domains(always_add_ip=False, always_add_all_domains=False) -> List
156
173
 
157
174
 
158
175
  def get_proxy_domains(domain):
159
- # if hconfig(ConfigEnum.is_parent):
160
- # from hiddifypanel.models.parent_domain import ParentDomain
161
- # db_domain = ParentDomain.query.filter(ParentDomain.domain == domain).first() or ParentDomain(domain=domain, show_domains=[])
162
- # else:
163
- db_domain = Domain.query.filter(Domain.domain == domain, Domain.child_id == Child.current.id).first()
176
+ db_domain = Domain.query.filter(Domain.domain == domain, Domain.child_id == Child.current().id).first()
164
177
  if not db_domain:
165
178
  db_domain = Domain(domain=domain, mode=DomainType.direct, cdn_ip='', show_domains=[])
166
179
  return get_proxy_domains_db(db_domain)
@@ -202,11 +215,11 @@ def add_or_update_domain(commit=True, child_id=0, **domain):
202
215
  db.session.commit()
203
216
 
204
217
 
205
- def bulk_register_domains(domains, commit=True, remove=False, override_child_unique_id=None):
218
+ def bulk_register_domains(domains, commit=True, remove=False, force_child_unique_id: str | None = None):
206
219
  from hiddifypanel.panel import hiddify
207
220
  child_ids = {}
208
221
  for domain in domains:
209
- child_id = hiddify.get_child(unique_id=None)
222
+ child_id = hiddify.get_child(unique_id=force_child_unique_id)
210
223
  child_ids[child_id] = 1
211
224
  add_or_update_domain(commit=False, child_id=child_id, **domain)
212
225
  if remove and len(child_ids):
@@ -215,10 +228,5 @@ def bulk_register_domains(domains, commit=True, remove=False, override_child_uni
215
228
  if d.domain not in dd:
216
229
  db.session.delete(d)
217
230
 
218
- # if commit:
219
- db.session.commit()
220
- for domain in domains:
221
- child_id = hiddify.get_child(unique_id=None)
222
- add_or_update_domain(commit=False, child_id=child_id, **domain)
223
231
  if commit:
224
232
  db.session.commit()
@@ -1,6 +1,6 @@
1
1
  # from hiddifypanel.models.domain import Domain
2
2
  # from sqlalchemy.orm import backref
3
- # from sqlalchemy_serializer import SerializerMixin
3
+ #
4
4
 
5
5
  # from hiddifypanel.database import db
6
6
 
@@ -10,7 +10,7 @@
10
10
  # )
11
11
 
12
12
 
13
- # class ParentDomain(db.Model, SerializerMixin):
13
+ # class ParentDomain(db.Model):
14
14
  # id = db.Column(db.Integer, primary_key=True, autoincrement=True)
15
15
  # domain = db.Column(db.String(200), nullable=False, unique=True)
16
16
  # alias = db.Column(db.String(200), nullable=False, unique=False)
@@ -1,4 +1,4 @@
1
- from sqlalchemy_serializer import SerializerMixin
1
+
2
2
  from strenum import StrEnum
3
3
  from enum import auto
4
4
  from sqlalchemy import Column, String, Integer, Boolean, Enum, ForeignKey
@@ -57,7 +57,7 @@ class ProxyL3(StrEnum):
57
57
  custom = auto()
58
58
 
59
59
 
60
- class Proxy(db.Model, SerializerMixin): # type: ignore
60
+ class Proxy(db.Model): # type: ignore
61
61
  id = Column(Integer, primary_key=True, autoincrement=True)
62
62
  child_id = Column(Integer, ForeignKey('child.id'), default=0)
63
63
  name = Column(String(200), nullable=False, unique=False)
@@ -102,10 +102,19 @@ class Proxy(db.Model, SerializerMixin): # type: ignore
102
102
  db.session.commit() # type: ignore
103
103
 
104
104
  @staticmethod
105
- def bulk_register(proxies, commit=True, override_child_unique_id=None):
105
+ def from_schema(schema):
106
+ return schema.dump(Proxy())
107
+
108
+ def to_schema(self):
109
+ proxy_dict = self.to_dict()
110
+ from hiddifypanel.panel.commercial.restapi.v2.parent.schema import ProxySchema
111
+ return ProxySchema().load(proxy_dict)
112
+
113
+ @staticmethod
114
+ def bulk_register(proxies, commit=True, force_child_unique_id: str | None = None):
106
115
  from hiddifypanel.panel import hiddify
107
116
  for proxy in proxies:
108
- child_id = hiddify.get_child(unique_id=None)
117
+ child_id = hiddify.get_child(unique_id=force_child_unique_id)
109
118
  Proxy.add_or_update(commit=False, child_id=child_id, **proxy)
110
119
  if commit:
111
120
  db.session.commit() # type: ignore
@@ -1,11 +1,10 @@
1
1
  import datetime
2
2
 
3
- from sqlalchemy_serializer import SerializerMixin
4
3
 
5
4
  from hiddifypanel.database import db
6
5
 
7
6
 
8
- class Report(db.Model, SerializerMixin):
7
+ class Report(db.Model):
9
8
  id = db.Column(db.Integer, primary_key=True, autoincrement=True)
10
9
  user_id = db.Column(db.Integer, db.ForeignKey('user.id'), default=0, nullable=False)
11
10
  asn_id = db.Column(db.String(200), nullable=False, unique=False)
@@ -19,7 +18,7 @@ class Report(db.Model, SerializerMixin):
19
18
  details = db.relationship('ReportDetail', cascade="all,delete", backref='report', lazy='dynamic',)
20
19
 
21
20
 
22
- class ReportDetail(db.Model, SerializerMixin):
21
+ class ReportDetail(db.Model):
23
22
  report_id = db.Column(db.Integer, db.ForeignKey('report.id'), primary_key=True, )
24
23
  proxy_id = db.Column(db.Integer, db.ForeignKey('proxy.id'), primary_key=True, )
25
24
  ping = db.Column(db.Integer, default=-1)
@@ -3,12 +3,12 @@ from datetime import timedelta, date
3
3
 
4
4
  from flask import g
5
5
  from sqlalchemy import func
6
- from sqlalchemy_serializer import SerializerMixin
6
+
7
7
 
8
8
  from hiddifypanel.database import db
9
9
 
10
10
 
11
- class DailyUsage(db.Model, SerializerMixin):
11
+ class DailyUsage(db.Model):
12
12
  id = db.Column(db.Integer, primary_key=True, autoincrement=True)
13
13
  date = db.Column(db.Date, default=datetime.date.today(), index=True)
14
14
  usage = db.Column(db.BigInteger, default=0, nullable=False)
@@ -2,7 +2,7 @@ import datetime
2
2
  from enum import auto
3
3
  from hiddifypanel.models.role import Role
4
4
  from dateutil import relativedelta
5
- from sqlalchemy_serializer import SerializerMixin
5
+
6
6
  from strenum import StrEnum
7
7
  from sqlalchemy import event
8
8
 
@@ -35,13 +35,13 @@ package_mode_dic = {
35
35
  }
36
36
 
37
37
 
38
- class UserDetail(db.Model, SerializerMixin):
38
+ class UserDetail(db.Model):
39
39
  id = db.Column(db.Integer, primary_key=True, autoincrement=True)
40
40
  user_id = db.Column(db.Integer, db.ForeignKey('user.id'), default=0, nullable=False)
41
41
  child_id = db.Column(db.Integer, db.ForeignKey('child.id'), default=0, nullable=False)
42
42
  last_online = db.Column(db.DateTime, nullable=False, default=datetime.datetime.min)
43
43
  current_usage = db.Column(db.BigInteger, default=0, nullable=False)
44
- connected_ips = db.Column(db.String(512), default='', nullable=False)
44
+ connected_devices = db.Column(db.String(512), default='', nullable=False)
45
45
 
46
46
  @property
47
47
  def current_usage_GB(self):
@@ -52,8 +52,8 @@ class UserDetail(db.Model, SerializerMixin):
52
52
  self.current_usage = (value or 0) * ONE_GIG
53
53
 
54
54
  @property
55
- def ips(self):
56
- return [] if not self.connected_ips else self.connected_ips.split(",")
55
+ def devices(self):
56
+ return [] if not self.connected_devices else self.connected_devices.split(",")
57
57
 
58
58
 
59
59
  class User(BaseAccount):
@@ -121,16 +121,16 @@ class User(BaseAccount):
121
121
  is_active = False
122
122
  elif self.remaining_days < 0:
123
123
  is_active = False
124
- elif len(self.ips) > max(3, self.max_ips):
124
+ elif len(self.devices) > max(3, self.max_ips):
125
125
  is_active = False
126
126
  return is_active
127
127
 
128
128
  @property
129
- def ips(self):
129
+ def devices(self):
130
130
  res = {}
131
131
  for detail in UserDetail.query.filter(UserDetail.user_id == self.id):
132
- for ip in detail.ips:
133
- res[ip] = 1
132
+ for device in detail.devices:
133
+ res[device] = 1
134
134
  return list(res.keys())
135
135
 
136
136
  def user_should_reset(self) -> bool:
@@ -256,6 +256,15 @@ class User(BaseAccount):
256
256
  db.session.commit()
257
257
  return dbuser
258
258
 
259
+ @staticmethod
260
+ def form_schema(schema):
261
+ return schema.dump(User())
262
+
263
+ def to_schema(self):
264
+ user_dict = self.to_dict()
265
+ from hiddifypanel.panel.commercial.restapi.v2.admin.user_api import UserSchema
266
+ return UserSchema().load(user_dict)
267
+
259
268
  def to_dict(self, convert_date=True, dump_id=False) -> dict:
260
269
  base = super().to_dict()
261
270
  from hiddifypanel import hutils
@@ -61,11 +61,9 @@ class Actions(FlaskView):
61
61
  def reinstall2(self, complete_install=True, domain_changed=False):
62
62
  if int(hconfig(ConfigEnum.db_version)) < 9:
63
63
  return ("Please update your panel before this action.")
64
- # if hconfig(ConfigEnum.parent_panel):
65
- # try:
66
- # hiddify_api.sync_child_to_parent()
67
- # except e as Exception:
68
- # hutils.flask.flash(_('can not sync child with parent panel')+" "+e)
64
+ if hutils.node.is_child():
65
+ if not hutils.node.child.sync_with_parent():
66
+ hutils.flask.flash(_('child.sync-failed'), 'danger') # type: ignore
69
67
 
70
68
  domain_changed = request.args.get("domain_changed", str(domain_changed)).lower() == "true"
71
69
  complete_install = request.args.get("complete_install", str(complete_install)).lower() == "true"
@@ -110,7 +108,7 @@ class Actions(FlaskView):
110
108
 
111
109
  @login_required(roles={Role.super_admin})
112
110
  def change_reality_keys(self):
113
- key = hiddify.generate_x25519_keys()
111
+ key = hutils.crypto.generate_x25519_keys()
114
112
  set_hconfig(ConfigEnum.reality_private_key, key['private_key'])
115
113
  set_hconfig(ConfigEnum.reality_public_key, key['public_key'])
116
114
  hutils.flask.flash_config_success(restart_mode=ApplyMode.restart, domain_changed=False)
@@ -7,7 +7,9 @@ from .adminlte import AdminLTEModelView
7
7
  from flask_babel import lazy_gettext as _
8
8
  from wtforms.validators import Regexp
9
9
  from flask_babel import gettext as __
10
- from flask import Markup, request # type: ignore
10
+ from flask import request # type: ignore
11
+ from markupsafe import Markup
12
+
11
13
  from flask import g
12
14
  import datetime
13
15
  from wtforms import SelectField
@@ -37,7 +39,7 @@ class SubAdminsField(SelectField):
37
39
 
38
40
  class AdminstratorAdmin(AdminLTEModelView):
39
41
  column_hide_backrefs = False
40
- column_list = ["name", 'UserLinks', 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'online_users', 'comment']
42
+ column_list = ["name", 'UserLinks', 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'online_users', 'comment',]
41
43
  form_columns = ["name", 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'comment', "uuid"]
42
44
  list_template = 'model/admin_list.html'
43
45
  # column_editable_list = ['name']
@@ -215,6 +217,7 @@ class AdminstratorAdmin(AdminLTEModelView):
215
217
  model.remove()
216
218
 
217
219
  def get_query_for_parent_admin(self):
220
+ # WHAT IS THIS?
218
221
  admin_user_id = self.get_pk_value()
219
222
  sub_admins_ids = set(recursive_sub_admins_ids(AdminUser.query.get(admin_user_id)))
220
223
  return AdminUser.query.filter(AdminUser.id.in_(sub_admins_ids)).with_entities(AdminUser.id, AdminUser.name)
@@ -236,3 +239,11 @@ class AdminstratorAdmin(AdminLTEModelView):
236
239
  del form.max_users
237
240
  del form.max_active_users
238
241
  del form.can_add_admin
242
+
243
+ def after_model_change(self, form, model, is_created):
244
+ if hutils.node.is_parent():
245
+ hutils.node.parent.send_sync_req_to_childs()
246
+
247
+ def after_model_delete(self, model):
248
+ if hutils.node.is_parent():
249
+ hutils.node.parent.send_sync_req_to_childs()
@@ -18,9 +18,9 @@ class Dashboard(FlaskView):
18
18
 
19
19
  @login_required(roles={Role.super_admin, Role.admin, Role.agent})
20
20
  def index(self):
21
-
22
21
  if hconfig(ConfigEnum.first_setup):
23
22
  return redirect(hurl_for("admin.QuickSetup:index"))
23
+
24
24
  if hiddifypanel.__release_date__ + datetime.timedelta(days=20) < datetime.datetime.now():
25
25
  hutils.flask.flash(_('This version of hiddify panel is outdated. Please update it from admin area.'), "danger") # type: ignore
26
26
  bot = None
@@ -34,20 +34,15 @@ class Dashboard(FlaskView):
34
34
  user_query = User.query
35
35
  if admin_id:
36
36
  user_query = user_query.filter(User.added_by == admin_id)
37
- if hconfig(ConfigEnum.is_parent):
37
+ if hutils.node.is_parent():
38
38
  childs = Child.query.filter(Child.id != 0).all()
39
39
  for c in childs:
40
40
  c.is_active = False
41
41
  for d in c.domains:
42
- if d.mode == DomainType.fake:
43
- continue
44
- remote = hiddify.get_account_panel_link(g.account, d.domain, child_id=c.id)
45
- d.is_active = hutils.network.check_connection_to_remote(remote)
42
+ d.is_active = hutils.node.parent.is_child_domain_active(c, d)
46
43
  if d.is_active:
47
44
  c.is_active = True
48
45
 
49
- # return render_template('parent_dash.html',childs=childs,bot=bot)
50
- # try:
51
46
  def_user = None if len(User.query.all()) > 1 else User.query.filter(User.name == 'default').first()
52
47
  domains = get_panel_domains()
53
48
  sslip_domains = [d.domain for d in domains if "sslip.io" in d.domain]
@@ -56,13 +51,13 @@ class Dashboard(FlaskView):
56
51
  quick_setup = hurl_for("admin.QuickSetup:index")
57
52
  hutils.flask.flash((_('It seems that you have not setup the system completely. <a class="btn btn-success" href="%(quick_setup)s">Click here</a> to complete setup.',
58
53
  quick_setup=quick_setup)), 'warning') # type: ignore
59
- if hconfig(ConfigEnum.is_parent):
54
+ if hutils.node.is_parent():
60
55
  hutils.flask.flash(
61
56
  _("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
62
57
  elif len(sslip_domains):
63
58
  hutils.flask.flash((_('It seems that you are using default domain (%(domain)s) which is not recommended.',
64
59
  domain=sslip_domains[0])), 'warning') # type: ignore
65
- if hconfig(ConfigEnum.is_parent):
60
+ if hutils.node.is_parent():
66
61
  hutils.flask.flash(
67
62
  _("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
68
63
  elif def_user: