hiddifypanel 10.14.0__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 (107) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/auth.py +15 -4
  4. hiddifypanel/base.py +11 -3
  5. hiddifypanel/cache.py +43 -25
  6. hiddifypanel/database.py +9 -0
  7. hiddifypanel/drivers/singbox_api.py +2 -14
  8. hiddifypanel/drivers/xray_api.py +0 -4
  9. hiddifypanel/hutils/__init__.py +1 -0
  10. hiddifypanel/hutils/convert.py +13 -2
  11. hiddifypanel/hutils/crypto.py +21 -2
  12. hiddifypanel/hutils/flask.py +18 -4
  13. hiddifypanel/hutils/importer/xui.py +5 -2
  14. hiddifypanel/hutils/node/__init__.py +3 -0
  15. hiddifypanel/hutils/node/api_client.py +76 -0
  16. hiddifypanel/hutils/node/child.py +147 -0
  17. hiddifypanel/hutils/node/parent.py +100 -0
  18. hiddifypanel/hutils/node/shared.py +65 -0
  19. hiddifypanel/hutils/proxy/shared.py +15 -3
  20. hiddifypanel/models/__init__.py +2 -2
  21. hiddifypanel/models/admin.py +14 -2
  22. hiddifypanel/models/base_account.py +3 -3
  23. hiddifypanel/models/child.py +30 -16
  24. hiddifypanel/models/config.py +39 -15
  25. hiddifypanel/models/config_enum.py +55 -8
  26. hiddifypanel/models/domain.py +28 -20
  27. hiddifypanel/models/parent_domain.py +2 -2
  28. hiddifypanel/models/proxy.py +13 -4
  29. hiddifypanel/models/report.py +2 -3
  30. hiddifypanel/models/usage.py +2 -2
  31. hiddifypanel/models/user.py +13 -4
  32. hiddifypanel/panel/admin/Actions.py +4 -6
  33. hiddifypanel/panel/admin/AdminstratorAdmin.py +13 -2
  34. hiddifypanel/panel/admin/Dashboard.py +5 -10
  35. hiddifypanel/panel/admin/DomainAdmin.py +12 -11
  36. hiddifypanel/panel/admin/NodeAdmin.py +6 -2
  37. hiddifypanel/panel/admin/ProxyAdmin.py +4 -3
  38. hiddifypanel/panel/admin/SettingAdmin.py +60 -21
  39. hiddifypanel/panel/admin/UserAdmin.py +10 -2
  40. hiddifypanel/panel/admin/templates/index.html +1 -1
  41. hiddifypanel/panel/admin/templates/parent_dash.html +2 -4
  42. hiddifypanel/panel/cli.py +16 -16
  43. hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +10 -5
  44. hiddifypanel/panel/commercial/__init__.py +7 -5
  45. hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +0 -5
  46. hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +2 -2
  47. hiddifypanel/panel/commercial/restapi/v2/admin/admin_log_api.py +4 -5
  48. hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +8 -35
  49. hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +4 -4
  50. hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +157 -0
  51. hiddifypanel/panel/commercial/restapi/v2/admin/server_status_api.py +3 -3
  52. hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +9 -73
  53. hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -1
  54. hiddifypanel/panel/commercial/restapi/v2/child/__init__.py +18 -0
  55. hiddifypanel/panel/commercial/restapi/v2/child/actions.py +63 -0
  56. hiddifypanel/panel/commercial/restapi/v2/child/register_parent_api.py +34 -0
  57. hiddifypanel/panel/commercial/restapi/v2/child/schema.py +7 -0
  58. hiddifypanel/panel/commercial/restapi/v2/child/sync_parent_api.py +21 -0
  59. hiddifypanel/panel/commercial/restapi/v2/panel/__init__.py +13 -0
  60. hiddifypanel/panel/commercial/restapi/v2/panel/info.py +18 -0
  61. hiddifypanel/panel/commercial/restapi/v2/panel/ping_pong.py +23 -0
  62. hiddifypanel/panel/commercial/restapi/v2/panel/schema.py +7 -0
  63. hiddifypanel/panel/commercial/restapi/v2/parent/__init__.py +16 -0
  64. hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +65 -0
  65. hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +115 -0
  66. hiddifypanel/panel/commercial/restapi/v2/parent/status_api.py +26 -0
  67. hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +53 -0
  68. hiddifypanel/panel/commercial/restapi/v2/parent/usage_api.py +57 -0
  69. hiddifypanel/panel/commercial/telegrambot/admin.py +1 -2
  70. hiddifypanel/panel/common.py +21 -6
  71. hiddifypanel/panel/hiddify.py +9 -80
  72. hiddifypanel/panel/init_db.py +43 -12
  73. hiddifypanel/panel/usage.py +28 -15
  74. hiddifypanel/panel/user/templates/home/usage.html +1 -1
  75. hiddifypanel/panel/user/templates/new.html +1 -1
  76. hiddifypanel/static/css/custom.css +13 -0
  77. hiddifypanel/static/images/hiddify.png +0 -0
  78. hiddifypanel/static/new/assets/{index-bce9b1a6.js → index-ccb9873c.js} +65 -65
  79. hiddifypanel/templates/admin-layout.html +24 -40
  80. hiddifypanel/templates/fake.html +298 -0
  81. hiddifypanel/templates/master.html +23 -41
  82. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  83. hiddifypanel/translations/en/LC_MESSAGES/messages.po +90 -0
  84. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  85. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +91 -1
  86. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  87. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +98 -6
  88. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  89. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +90 -0
  90. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  91. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +92 -2
  92. hiddifypanel/translations.i18n/en.json +56 -0
  93. hiddifypanel/translations.i18n/fa.json +57 -1
  94. hiddifypanel/translations.i18n/pt.json +63 -7
  95. hiddifypanel/translations.i18n/ru.json +56 -0
  96. hiddifypanel/translations.i18n/zh.json +58 -2
  97. {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/METADATA +47 -47
  98. {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/RECORD +104 -86
  99. hiddifypanel/panel/commercial/restapi/v2/DTO.py +0 -9
  100. hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py +0 -16
  101. hiddifypanel/panel/commercial/restapi/v2/hello/hello.py +0 -32
  102. /hiddifypanel/static/images/{hiddify1.png → hiddify-old.png} +0 -0
  103. /hiddifypanel/static/{new/assets/hiddify-logo-noroz-559c8dcb.png → images/hiddify2.png} +0 -0
  104. {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/LICENSE.md +0 -0
  105. {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/WHEEL +0 -0
  106. {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/entry_points.txt +0 -0
  107. {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,100 @@
1
+ from flask import g
2
+ from flask_babel import lazy_gettext as _
3
+ from typing import List
4
+ from loguru import logger
5
+
6
+ from hiddifypanel.models import Child, AdminUser, ConfigEnum, Domain, ChildMode, hconfig, get_panel_link
7
+ from hiddifypanel import hutils
8
+ from hiddifypanel.panel.commercial.restapi.v2.child.schema import RegisterWithParentInputSchema
9
+ from .api_client import NodeApiClient, NodeApiErrorSchema
10
+
11
+ from hiddifypanel.cache import cache
12
+
13
+
14
+ def request_childs_to_sync():
15
+ for c in Child.query.filter(Child.id != 0).all():
16
+ if not request_child_to_sync(c):
17
+ logger.error(f'{c.name}: {_("parent.sync-req-failed")}')
18
+ hutils.flask.flash(f'{c.name}: ' + _('parent.sync-req-failed'), 'danger')
19
+
20
+
21
+ def request_child_to_sync(child: Child) -> bool:
22
+ '''Requests to a child to sync itself with the current panel'''
23
+ child_domain = get_panel_link(child.id)
24
+ if not child_domain:
25
+ logger.error(f"Child {child.name} has no valid domain")
26
+ return False
27
+
28
+ child_admin_proxy_path = hconfig(ConfigEnum.proxy_path_admin, child.id)
29
+ base_url = f'https://{child_domain}/{child_admin_proxy_path}'
30
+ path = '/api/v2/child/sync-parent/'
31
+ res = NodeApiClient(base_url).post(path, payload=None, output=dict)
32
+ if isinstance(res, NodeApiErrorSchema):
33
+ logger.error(f"Error while requesting child {child.name} to sync: {res.msg}")
34
+ return False
35
+ if res['msg'] == 'ok':
36
+ logger.success(f"Successfully requested child {child.name} to sync")
37
+ cache.invalidate_all_cached_functions()
38
+ return True
39
+
40
+ logger.error(f"Request to child {child.name} to sync failed")
41
+ return False
42
+
43
+ # before using this function should check child version
44
+
45
+
46
+ # TODO: not used
47
+ def request_chlid_to_register(name: str, child_link: str, apikey: str) -> bool:
48
+ '''Requests to a child to register itself with the current panel'''
49
+ if not child_link or not apikey:
50
+ logger.error("Child link or apikey is empty")
51
+ return False
52
+ domain = get_panel_link()
53
+ if not domain:
54
+ logger.error("Domain is empty")
55
+ return False
56
+ from hiddifypanel.panel import hiddify
57
+
58
+ payload = RegisterWithParentInputSchema()
59
+ payload.parent_panel = hiddify.get_account_panel_link(AdminUser.by_uuid(g.account.uuid), domain.domain) # type: ignore
60
+ payload.apikey = payload.name = hconfig(ConfigEnum.unique_id)
61
+
62
+ logger.debug(f"Requesting child {name} to register")
63
+ res = NodeApiClient(child_link, apikey).post('/api/v2/child/register-parent/', payload, dict)
64
+ if isinstance(res, NodeApiErrorSchema):
65
+ logger.error(f"Error while requesting child {name} to register: {res.msg}")
66
+ return False
67
+
68
+ if res['msg'] == 'ok':
69
+ logger.success(f"Successfully requested child {name} to register")
70
+ cache.invalidate_all_cached_functions()
71
+ return True
72
+
73
+ logger.error(f"Request to child {name} to register failed")
74
+ return False
75
+
76
+
77
+ def is_child_domain_active(child: Child, domain: Domain) -> bool:
78
+ '''Checks whether a child's domain is responsive'''
79
+ if not domain.need_valid_ssl:
80
+ return False
81
+ child_admin_proxy_path = hconfig(ConfigEnum.proxy_path_admin, child.id)
82
+ if not child_admin_proxy_path:
83
+ return False
84
+
85
+ return hutils.node.is_panel_active(domain.domain, child_admin_proxy_path)
86
+
87
+
88
+ def get_child_active_domains(child: Child) -> List[Domain]:
89
+ actives = []
90
+ for d in child.domains:
91
+ if is_child_domain_active(child, d):
92
+ actives.append(d)
93
+ return actives
94
+
95
+
96
+ def is_child_active(child: Child) -> bool:
97
+ for d in child.domains:
98
+ if is_child_domain_active(child, d):
99
+ return True
100
+ return False
@@ -0,0 +1,65 @@
1
+ from loguru import logger
2
+
3
+ from hiddifypanel.models import hconfig, ConfigEnum, PanelMode, User
4
+ from hiddifypanel.cache import cache
5
+ from hiddifypanel.panel.commercial.restapi.v2.parent.schema import UsageInputOutputSchema, UsageData
6
+ from hiddifypanel.panel.commercial.restapi.v2.panel.schema import PanelInfoOutputSchema
7
+ from .api_client import NodeApiClient, NodeApiErrorSchema
8
+
9
+
10
+ def is_child() -> bool:
11
+ return hconfig(ConfigEnum.panel_mode) == PanelMode.child
12
+
13
+
14
+ def is_parent() -> bool:
15
+ return hconfig(ConfigEnum.panel_mode) == PanelMode.parent
16
+
17
+ # region usage
18
+
19
+
20
+ def get_users_usage_data_for_api() -> UsageInputOutputSchema:
21
+ res = UsageInputOutputSchema()
22
+ res.usages = [] # type: ignore
23
+ for u in User.query.all():
24
+ usage_data = UsageData()
25
+ usage_data.uuid = u.uuid
26
+ usage_data.usage = u.current_usage
27
+ usage_data.devices = u.devices
28
+ res.usages.append(usage_data) # type: ignore
29
+ return res
30
+
31
+
32
+ def convert_usage_api_response_to_dict(data: dict) -> dict:
33
+ converted = {}
34
+ for i in data['usages']: # type: ignore
35
+ converted[str(i['uuid'])] = {
36
+ 'usage': i['usage'],
37
+ 'devices': ','.join(i['devices']) # type: ignore
38
+ }
39
+ return converted
40
+
41
+ # endregion
42
+
43
+
44
+ #@cache.cache(ttl=150)
45
+ def is_panel_active(domain: str, proxy_path: str,apikey:str|None = None) -> bool:
46
+ base_url = f'https://{domain}/{proxy_path}'
47
+ res = NodeApiClient(base_url,apikey).get('/api/v2/panel/ping/', dict)
48
+ if isinstance(res, NodeApiErrorSchema):
49
+ logger.error(f"Error while checking if panel is active: {res.msg}")
50
+ return False
51
+ if 'PONG' in res['msg']:
52
+ logger.debug(f"Panel is active: {res['msg']}")
53
+ return True
54
+ logger.debug("Panel is not active")
55
+ return False
56
+
57
+
58
+ #@cache.cache(300)
59
+ def get_panel_info(domain: str, proxy_path: str,apikey:str|None = None) -> dict | None:
60
+ base_url = f'https://{domain}/{proxy_path}'
61
+ res = NodeApiClient(base_url,apikey).get('/api/v2/panel/info/', PanelInfoOutputSchema)
62
+ if isinstance(res, NodeApiErrorSchema):
63
+ logger.error(f"Error while getting panel info from {domain}: {res.msg}")
64
+ return None
65
+ return res
@@ -5,7 +5,7 @@ import re
5
5
  import json
6
6
  from ipaddress import IPv4Address, IPv6Address
7
7
  from hiddifypanel.cache import cache
8
- from hiddifypanel.models import Proxy, ProxyProto, ProxyTransport, ProxyCDN, Domain, DomainType, ConfigEnum, hconfig, get_hconfigs
8
+ from hiddifypanel.models import Proxy, ProxyProto, ProxyL3, ProxyTransport, ProxyCDN, Domain, DomainType, ConfigEnum, hconfig, get_hconfigs
9
9
  from hiddifypanel import hutils
10
10
 
11
11
 
@@ -124,16 +124,28 @@ def get_proxies(child_id: int = 0, only_enabled=False) -> list['Proxy']:
124
124
  proxies = [c for c in proxies if 'ssr' != c.proto]
125
125
  if not hconfig(ConfigEnum.vmess_enable, child_id):
126
126
  proxies = [c for c in proxies if 'vmess' not in c.proto]
127
+ if not hconfig(ConfigEnum.vless_enable, child_id):
128
+ proxies = [c for c in proxies if 'vless' not in c.proto]
129
+ if not hconfig(ConfigEnum.trojan_enable, child_id):
130
+ proxies = [c for c in proxies if 'trojan' not in c.proto]
127
131
  if not hconfig(ConfigEnum.httpupgrade_enable, child_id):
128
132
  proxies = [c for c in proxies if ProxyTransport.httpupgrade not in c.transport]
129
133
  if not hconfig(ConfigEnum.ws_enable, child_id):
130
134
  proxies = [c for c in proxies if ProxyTransport.WS not in c.transport]
131
-
135
+ if not hconfig(ConfigEnum.xtls_enable, child_id):
136
+ proxies = [c for c in proxies if ProxyTransport.XTLS not in c.transport]
132
137
  if not hconfig(ConfigEnum.grpc_enable, child_id):
133
138
  proxies = [c for c in proxies if ProxyTransport.grpc not in c.transport]
139
+ if not hconfig(ConfigEnum.tcp_enable, child_id):
140
+ proxies = [c for c in proxies if 'tcp' not in c.transport]
141
+ if not hconfig(ConfigEnum.h2_enable, child_id):
142
+ proxies = [c for c in proxies if 'h2' not in c.transport and c.l3 not in [ProxyL3.tls_h2_h1, ProxyL3.tls_h2]]
134
143
  if not hconfig(ConfigEnum.kcp_enable, child_id):
135
144
  proxies = [c for c in proxies if 'kcp' not in c.l3]
136
-
145
+ if not hconfig(ConfigEnum.reality_enable, child_id):
146
+ proxies = [c for c in proxies if 'reality' not in c.l3]
147
+ if not hconfig(ConfigEnum.quic_enable, child_id):
148
+ proxies = [c for c in proxies if 'h3_quic' not in c.l3]
137
149
  if not hconfig(ConfigEnum.http_proxy_enable, child_id):
138
150
  proxies = [c for c in proxies if 'http' != c.l3]
139
151
 
@@ -1,10 +1,10 @@
1
1
  from .role import Role, AccountType
2
2
  from .child import Child, ChildMode
3
- from .config_enum import ConfigCategory, ConfigEnum, Lang, ApplyMode
3
+ from .config_enum import ConfigCategory, ConfigEnum, Lang, ApplyMode, PanelMode, LogLevel
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
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
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
@@ -7,7 +7,7 @@ from strenum import StrEnum
7
7
  from apiflask import abort
8
8
  from flask_babel import gettext as __
9
9
  from flask_babel import lazy_gettext as _
10
- from hiddifypanel.database import db
10
+ from hiddifypanel.database import db, db_execute
11
11
  from hiddifypanel.models.role import Role
12
12
  from hiddifypanel.models.base_account import BaseAccount
13
13
 
@@ -50,6 +50,15 @@ class AdminUser(BaseAccount):
50
50
  return Role.agent
51
51
  return None
52
52
 
53
+ @staticmethod
54
+ def form_schema(schema):
55
+ return schema.dump(AdminUser())
56
+
57
+ def to_schema(self):
58
+ admin_dict = self.to_dict()
59
+ from hiddifypanel.panel.commercial.restapi.v2.admin.admin_user_api import AdminSchema
60
+ return AdminSchema().load(admin_dict)
61
+
53
62
  def get_id(self) -> str | None:
54
63
  return f'admin_{self.id}'
55
64
 
@@ -57,10 +66,12 @@ class AdminUser(BaseAccount):
57
66
  base = super().to_dict()
58
67
  if dump_id:
59
68
  base['id'] = self.id
69
+ from hiddifypanel.models import hconfig, ConfigEnum
60
70
  return {**base,
61
71
  'mode': self.mode,
62
72
  'can_add_admin': self.can_add_admin,
63
73
  'parent_admin_uuid': self.parent_admin.uuid if self.parent_admin else None,
74
+ 'lang': hconfig(ConfigEnum.admin_lang)
64
75
  }
65
76
 
66
77
  @classmethod
@@ -144,7 +155,8 @@ class AdminUser(BaseAccount):
144
155
  if not admin:
145
156
  db.session.add(AdminUser(id=1, uuid=str(uuid.uuid4()), name="Owner", mode=AdminMode.super_admin, comment=""))
146
157
  db.session.commit()
147
- db.engine.execute("update admin_user set id=1 where name='Owner'")
158
+
159
+ db_execute("update admin_user set id=1 where name='Owner'")
148
160
  admin = AdminUser.by_id(1)
149
161
 
150
162
  return admin
@@ -2,13 +2,13 @@ import datetime
2
2
  import uuid
3
3
  from hiddifypanel.models.role import Role
4
4
  from sqlalchemy import Column, String, BigInteger, Enum
5
- from sqlalchemy_serializer import SerializerMixin
5
+
6
6
  from flask_login import UserMixin as FlaskLoginUserMixin
7
7
  from hiddifypanel.models import Lang
8
8
  from hiddifypanel.database import db
9
9
 
10
10
 
11
- class BaseAccount(db.Model, SerializerMixin, FlaskLoginUserMixin): # type: ignore
11
+ class BaseAccount(db.Model, FlaskLoginUserMixin): # type: ignore
12
12
  __abstract__ = True
13
13
  uuid = Column(String(36), default=lambda: str(uuid.uuid4()), nullable=False, unique=True, index=True)
14
14
  name = Column(String(512), nullable=False, default='')
@@ -74,7 +74,7 @@ class BaseAccount(db.Model, SerializerMixin, FlaskLoginUserMixin): # type: igno
74
74
  for u in accounts:
75
75
  cls.add_or_update(commit=False, **u)
76
76
  if remove:
77
- dd = {u['uuid']: 1 for u in accounts}
77
+ dd = {str(u['uuid']): 1 for u in accounts}
78
78
  for d in cls.query.all():
79
79
  if d.uuid not in dd:
80
80
  db.session.delete(d) # type: ignore
@@ -1,30 +1,34 @@
1
1
  from __future__ import annotations
2
2
  import uuid
3
- from sqlalchemy_serializer import SerializerMixin
3
+
4
+ from sqlalchemy import Column, Integer, String, Enum, text
4
5
  from enum import auto
5
6
  from strenum import StrEnum
6
7
  from flask import g, has_app_context
7
8
 
8
9
 
9
- from hiddifypanel.database import db
10
+ from hiddifypanel.database import db, db_execute
10
11
 
11
12
 
12
13
  class ChildMode(StrEnum):
13
14
  virtual = auto()
14
- remote = auto()
15
+ remote = auto() # it's child
16
+ parent = auto()
17
+
18
+ # the child model is node
15
19
 
16
20
 
17
- class Child(db.Model, SerializerMixin):
18
- id = db.Column(db.Integer, primary_key=True, autoincrement=True)
19
- name = db.Column(db.String(200), nullable=False, unique=True)
20
- mode = db.Column(db.Enum(ChildMode), nullable=False, default=ChildMode.virtual)
21
+ class Child(db.Model): # type: ignore
22
+ id = Column(Integer, primary_key=True, autoincrement=True)
23
+ name = Column(String(200), nullable=False, unique=False)
24
+ mode = Column(Enum(ChildMode), nullable=False, default=ChildMode.virtual)
21
25
  # ip = db.Column(db.String(200), nullable=False, unique=True)
22
- unique_id = db.Column(db.String(200), nullable=False, default=lambda: str(uuid.uuid4()), unique=True)
23
- domains = db.relationship('Domain', cascade="all,delete", backref='child')
24
- proxies = db.relationship('Proxy', cascade="all,delete", backref='child')
25
- boolconfigs = db.relationship('BoolConfig', cascade="all,delete", backref='child')
26
- strconfigs = db.relationship('StrConfig', cascade="all,delete", backref='child')
27
- dailyusages = db.relationship('DailyUsage', cascade="all,delete", backref='child')
26
+ unique_id = Column(String(200), nullable=False, default=lambda: str(uuid.uuid4()), unique=True)
27
+ domains = db.relationship('Domain', cascade="all,delete", backref='child') # type: ignore
28
+ proxies = db.relationship('Proxy', cascade="all,delete", backref='child') # type: ignore
29
+ boolconfigs = db.relationship('BoolConfig', cascade="all,delete", backref='child') # type: ignore
30
+ strconfigs = db.relationship('StrConfig', cascade="all,delete", backref='child') # type: ignore
31
+ dailyusages = db.relationship('DailyUsage', cascade="all,delete", backref='child') # type: ignore
28
32
 
29
33
  def to_dict(self):
30
34
  return {
@@ -55,11 +59,14 @@ class Child(db.Model, SerializerMixin):
55
59
  db.session.commit()
56
60
 
57
61
  @classmethod
58
- def by_id(cls, id: int) -> "Child":
62
+ def by_id(cls, id: int) -> 'Child':
59
63
  return Child.query.filter(Child.id == id).first()
60
64
 
61
65
  @classmethod
62
- @property
66
+ def by_unique_id(cls, unique_id: str) -> 'Child':
67
+ return Child.query.filter(Child.unique_id == unique_id).first()
68
+
69
+ @classmethod
63
70
  def current(cls) -> "Child":
64
71
  if has_app_context() and hasattr(g, "child"):
65
72
  return g.child
@@ -68,6 +75,13 @@ class Child(db.Model, SerializerMixin):
68
75
  tmp_uuid = str(uuid.uuid4())
69
76
  db.session.add(Child(id=0, unique_id=tmp_uuid, name="Root"))
70
77
  db.session.commit()
71
- db.engine.execute(f'update child set id=0 where unique_id="{tmp_uuid}"')
78
+ db_execute(f"update child set id=0 where unique_id='{tmp_uuid}'")
72
79
  child = Child.by_id(0)
80
+ print("child-=======", child)
73
81
  return child
82
+
83
+ @classmethod
84
+ @property
85
+ def node(cls) -> "Child | None":
86
+ if has_app_context() and hasattr(g, "node"):
87
+ return g.node
@@ -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