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.
- hiddifypanel/VERSION +1 -1
- hiddifypanel/VERSION.py +2 -2
- hiddifypanel/auth.py +15 -4
- hiddifypanel/base.py +11 -3
- hiddifypanel/cache.py +43 -25
- hiddifypanel/database.py +9 -0
- hiddifypanel/drivers/singbox_api.py +2 -14
- hiddifypanel/drivers/xray_api.py +0 -4
- hiddifypanel/hutils/__init__.py +1 -0
- hiddifypanel/hutils/convert.py +13 -2
- hiddifypanel/hutils/crypto.py +21 -2
- hiddifypanel/hutils/flask.py +18 -4
- hiddifypanel/hutils/importer/xui.py +5 -2
- hiddifypanel/hutils/node/__init__.py +3 -0
- hiddifypanel/hutils/node/api_client.py +76 -0
- hiddifypanel/hutils/node/child.py +147 -0
- hiddifypanel/hutils/node/parent.py +100 -0
- hiddifypanel/hutils/node/shared.py +65 -0
- hiddifypanel/hutils/proxy/shared.py +15 -3
- hiddifypanel/models/__init__.py +2 -2
- hiddifypanel/models/admin.py +14 -2
- hiddifypanel/models/base_account.py +3 -3
- hiddifypanel/models/child.py +30 -16
- hiddifypanel/models/config.py +39 -15
- hiddifypanel/models/config_enum.py +55 -8
- hiddifypanel/models/domain.py +28 -20
- hiddifypanel/models/parent_domain.py +2 -2
- hiddifypanel/models/proxy.py +13 -4
- hiddifypanel/models/report.py +2 -3
- hiddifypanel/models/usage.py +2 -2
- hiddifypanel/models/user.py +13 -4
- hiddifypanel/panel/admin/Actions.py +4 -6
- hiddifypanel/panel/admin/AdminstratorAdmin.py +13 -2
- hiddifypanel/panel/admin/Dashboard.py +5 -10
- hiddifypanel/panel/admin/DomainAdmin.py +12 -11
- hiddifypanel/panel/admin/NodeAdmin.py +6 -2
- hiddifypanel/panel/admin/ProxyAdmin.py +4 -3
- hiddifypanel/panel/admin/SettingAdmin.py +60 -21
- hiddifypanel/panel/admin/UserAdmin.py +10 -2
- hiddifypanel/panel/admin/templates/index.html +1 -1
- hiddifypanel/panel/admin/templates/parent_dash.html +2 -4
- hiddifypanel/panel/cli.py +16 -16
- hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +10 -5
- hiddifypanel/panel/commercial/__init__.py +7 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +0 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +2 -2
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_log_api.py +4 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +8 -35
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +4 -4
- hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +157 -0
- hiddifypanel/panel/commercial/restapi/v2/admin/server_status_api.py +3 -3
- hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +9 -73
- hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -1
- hiddifypanel/panel/commercial/restapi/v2/child/__init__.py +18 -0
- hiddifypanel/panel/commercial/restapi/v2/child/actions.py +63 -0
- hiddifypanel/panel/commercial/restapi/v2/child/register_parent_api.py +34 -0
- hiddifypanel/panel/commercial/restapi/v2/child/schema.py +7 -0
- hiddifypanel/panel/commercial/restapi/v2/child/sync_parent_api.py +21 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/__init__.py +13 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/info.py +18 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/ping_pong.py +23 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/schema.py +7 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/__init__.py +16 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +65 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +115 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/status_api.py +26 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +53 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/usage_api.py +57 -0
- hiddifypanel/panel/commercial/telegrambot/admin.py +1 -2
- hiddifypanel/panel/common.py +21 -6
- hiddifypanel/panel/hiddify.py +9 -80
- hiddifypanel/panel/init_db.py +43 -12
- hiddifypanel/panel/usage.py +28 -15
- hiddifypanel/panel/user/templates/home/usage.html +1 -1
- hiddifypanel/panel/user/templates/new.html +1 -1
- hiddifypanel/static/css/custom.css +13 -0
- hiddifypanel/static/images/hiddify.png +0 -0
- hiddifypanel/static/new/assets/{index-bce9b1a6.js → index-ccb9873c.js} +65 -65
- hiddifypanel/templates/admin-layout.html +24 -40
- hiddifypanel/templates/fake.html +298 -0
- hiddifypanel/templates/master.html +23 -41
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +90 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +91 -1
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +98 -6
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +90 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +92 -2
- hiddifypanel/translations.i18n/en.json +56 -0
- hiddifypanel/translations.i18n/fa.json +57 -1
- hiddifypanel/translations.i18n/pt.json +63 -7
- hiddifypanel/translations.i18n/ru.json +56 -0
- hiddifypanel/translations.i18n/zh.json +58 -2
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/METADATA +47 -47
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/RECORD +104 -86
- hiddifypanel/panel/commercial/restapi/v2/DTO.py +0 -9
- hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py +0 -16
- hiddifypanel/panel/commercial/restapi/v2/hello/hello.py +0 -32
- /hiddifypanel/static/images/{hiddify1.png → hiddify-old.png} +0 -0
- /hiddifypanel/static/{new/assets/hiddify-logo-noroz-559c8dcb.png → images/hiddify2.png} +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/WHEEL +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/entry_points.txt +0 -0
- {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
|
|
hiddifypanel/models/__init__.py
CHANGED
@@ -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
|
hiddifypanel/models/admin.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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,
|
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
|
hiddifypanel/models/child.py
CHANGED
@@ -1,30 +1,34 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import uuid
|
3
|
-
|
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
|
18
|
-
id =
|
19
|
-
name =
|
20
|
-
mode =
|
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 =
|
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) ->
|
62
|
+
def by_id(cls, id: int) -> 'Child':
|
59
63
|
return Child.query.filter(Child.id == id).first()
|
60
64
|
|
61
65
|
@classmethod
|
62
|
-
|
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
|
-
|
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
|
hiddifypanel/models/config.py
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
from
|
1
|
+
from typing import Optional
|
2
|
+
from hiddifypanel.models.config_enum import ConfigEnum, LogLevel, PanelMode, Lang
|
2
3
|
from flask import g
|
3
|
-
|
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
|
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
|
-
|
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
|
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
|
67
|
-
|
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,
|
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=
|
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
|
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=
|
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
|
-
|
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 =
|
119
|
-
admin_lang =
|
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
|