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.
- hiddifypanel/VERSION +1 -1
- hiddifypanel/VERSION.py +2 -2
- hiddifypanel/auth.py +15 -4
- hiddifypanel/base.py +58 -50
- hiddifypanel/cache.py +43 -25
- hiddifypanel/database.py +9 -0
- hiddifypanel/drivers/abstract_driver.py +2 -0
- hiddifypanel/drivers/singbox_api.py +17 -15
- hiddifypanel/drivers/ssh_liberty_bridge_api.py +2 -0
- hiddifypanel/drivers/user_driver.py +12 -6
- hiddifypanel/drivers/wireguard_api.py +2 -0
- hiddifypanel/drivers/xray_api.py +14 -9
- hiddifypanel/hutils/__init__.py +1 -0
- hiddifypanel/hutils/convert.py +13 -2
- hiddifypanel/hutils/crypto.py +21 -2
- hiddifypanel/hutils/flask.py +19 -5
- 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 +18 -9
- 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 +84 -38
- hiddifypanel/panel/usage.py +33 -18
- hiddifypanel/panel/user/templates/home/usage.html +1 -1
- hiddifypanel/panel/user/templates/new.html +2 -2
- hiddifypanel/static/css/custom.css +13 -0
- hiddifypanel/static/images/hiddify.png +0 -0
- hiddifypanel/static/images/hiddify2.png +0 -0
- hiddifypanel/static/new/assets/hiddify-logo-7617d937.png +0 -0
- hiddifypanel/static/new/assets/{index-4510b616.js → index-ccb9873c.js} +2 -2
- hiddifypanel/static/new/assets/index-fa00de9a.css +1 -0
- hiddifypanel/static/new/i18n/en.json +6 -6
- hiddifypanel/static/new/i18n/fa.json +1 -1
- hiddifypanel/templates/admin-layout.html +24 -40
- hiddifypanel/templates/fake.html +22 -0
- hiddifypanel/templates/master.html +24 -42
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +95 -5
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +96 -4
- 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 +91 -1
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +92 -2
- hiddifypanel/translations.i18n/en.json +61 -5
- hiddifypanel/translations.i18n/fa.json +60 -4
- hiddifypanel/translations.i18n/pt.json +63 -7
- hiddifypanel/translations.i18n/ru.json +57 -1
- hiddifypanel/translations.i18n/zh.json +58 -2
- {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/METADATA +47 -47
- {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/RECORD +112 -94
- 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/new/assets/hiddify-logo-7617d937_old.png +0 -0
- hiddifypanel/static/new/assets/index-669b32c8.css +0 -1
- /hiddifypanel/static/images/{hiddify1.png → hiddify-old.png} +0 -0
- {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/WHEEL +0 -0
- {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-10.12.1.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
from apiflask import fields, Schema
|
2
|
+
from marshmallow import ValidationError
|
3
|
+
|
4
|
+
from hiddifypanel.models import DomainType, ProxyProto, ProxyL3, ProxyTransport, ProxyCDN, ConfigEnum, ChildMode
|
5
|
+
from hiddifypanel.panel.commercial.restapi.v2.admin.schema import UserSchema, AdminSchema
|
6
|
+
|
7
|
+
|
8
|
+
def hconfig_key_validator(value):
|
9
|
+
if value not in [c.name for c in ConfigEnum]:
|
10
|
+
raise ValidationError(f"{value} is not a valid hconfig key.")
|
11
|
+
return value
|
12
|
+
|
13
|
+
|
14
|
+
class DomainSchema(Schema):
|
15
|
+
child_unique_id = fields.String(description="The child's unique id")
|
16
|
+
domain = fields.String(required=True, description="The domain name")
|
17
|
+
alias = fields.String(description="The domain alias", allow_none=True)
|
18
|
+
sub_link_only = fields.Boolean(required=True, description="Is the domain sub link only")
|
19
|
+
mode = fields.Enum(DomainType, required=True, description="The domain type")
|
20
|
+
cdn_ip = fields.String(description="The cdn ip", allow_none=True)
|
21
|
+
grpc = fields.Boolean(required=True, description="Is the domain grpc")
|
22
|
+
servernames = fields.String(description="The servernames", allow_none=True)
|
23
|
+
show_domains = fields.List(fields.String(), desciption="The list of domains to show")
|
24
|
+
|
25
|
+
|
26
|
+
class ProxySchema(Schema):
|
27
|
+
child_unique_id = fields.String(description="The child's unique id")
|
28
|
+
name = fields.String(required=True, description="The proxy name")
|
29
|
+
enable = fields.Boolean(required=True, description="Is the proxy enabled")
|
30
|
+
proto = fields.Enum(ProxyProto, required=True, description="The proxy protocol")
|
31
|
+
l3 = fields.Enum(ProxyL3, required=True, description="The proxy l3")
|
32
|
+
transport = fields.Enum(ProxyTransport, required=True, description="The proxy transport")
|
33
|
+
cdn = fields.Enum(ProxyCDN, required=True, description="The proxy cdn")
|
34
|
+
|
35
|
+
|
36
|
+
class StringOrBooleanField(fields.Field):
|
37
|
+
def _deserialize(self, value, attr, data, **kwargs):
|
38
|
+
if isinstance(value, (str, bool)):
|
39
|
+
return str(value)
|
40
|
+
else:
|
41
|
+
raise ValidationError("Value must be a string or a boolean.")
|
42
|
+
|
43
|
+
def _serialize(self, value, attr, obj, **kwargs):
|
44
|
+
return value
|
45
|
+
|
46
|
+
|
47
|
+
class HConfigSchema(Schema):
|
48
|
+
child_unique_id = fields.String(description="The child's unique id")
|
49
|
+
key = fields.String(required=True, description="The config key", validate=hconfig_key_validator) # type: ignore
|
50
|
+
value = StringOrBooleanField(required=True, description="The config value")
|
51
|
+
|
52
|
+
|
53
|
+
# region usage
|
54
|
+
class UsageData(Schema):
|
55
|
+
uuid = fields.UUID(required=True, desciption="The user uuid")
|
56
|
+
usage = fields.Integer(required=True, description="The user usage in bytes")
|
57
|
+
devices = fields.List(fields.String(required=True, description="The user connected devices"))
|
58
|
+
|
59
|
+
|
60
|
+
class UsageInputOutputSchema(Schema):
|
61
|
+
usages = fields.List(fields.Nested(UsageData), required=True, description="The list of usages")
|
62
|
+
# endregion
|
63
|
+
|
64
|
+
|
65
|
+
# region sync
|
66
|
+
class SyncInputSchema(Schema):
|
67
|
+
# users = fields.List(fields.Nested(UserSchema),required=True,description="The list of users")
|
68
|
+
domains = fields.List(fields.Nested(DomainSchema), required=True, description="The list of domains")
|
69
|
+
proxies = fields.List(fields.Nested(ProxySchema), required=True, description="The list of proxies")
|
70
|
+
# parent_domains = fields.List(fields.Nested(ParentDomainSchema),required=True,description="The list of parent domains")
|
71
|
+
# admin_users = fields.List(fields.Nested(AdminSchema),required=True,description="The list of admin users")
|
72
|
+
hconfigs = fields.List(fields.Nested(HConfigSchema), required=True, description="The list of configs")
|
73
|
+
|
74
|
+
|
75
|
+
class SyncOutputSchema(Schema):
|
76
|
+
users = fields.List(fields.Nested(UserSchema), required=True, description="The list of users")
|
77
|
+
admin_users = fields.List(fields.Nested(AdminSchema), required=True, description="The list of admin users")
|
78
|
+
|
79
|
+
# endregion
|
80
|
+
|
81
|
+
|
82
|
+
# region child status
|
83
|
+
class ChildStatusInputSchema(Schema):
|
84
|
+
child_unique_id = fields.String(required=True, description="The child's unique id")
|
85
|
+
|
86
|
+
|
87
|
+
class ChildStatusOutputSchema(Schema):
|
88
|
+
existance = fields.Boolean(required=True, description="Whether child exists")
|
89
|
+
|
90
|
+
# end region
|
91
|
+
|
92
|
+
|
93
|
+
# region register
|
94
|
+
|
95
|
+
class RegisterDataSchema(Schema):
|
96
|
+
users = fields.List(fields.Nested(UserSchema), required=True, description="The list of users")
|
97
|
+
domains = fields.List(fields.Nested(DomainSchema), required=True, description="The list of domains")
|
98
|
+
proxies = fields.List(fields.Nested(ProxySchema), required=True, description="The list of proxies")
|
99
|
+
admin_users = fields.List(fields.Nested(AdminSchema), required=True, description="The list of admin users")
|
100
|
+
hconfigs = fields.List(fields.Nested(HConfigSchema), required=True, description="The list of configs")
|
101
|
+
|
102
|
+
|
103
|
+
class RegisterInputSchema(Schema):
|
104
|
+
panel_data = fields.Nested(RegisterDataSchema, required=True, description="The child's data")
|
105
|
+
unique_id = fields.String(required=True, description="The child's unique id")
|
106
|
+
name = fields.String(required=True, description="The child's name")
|
107
|
+
mode = fields.Enum(ChildMode, required=True, description="The child's mode")
|
108
|
+
|
109
|
+
|
110
|
+
class RegisterOutputSchema(Schema):
|
111
|
+
parent_unique_id = fields.String(description="The parent's unique id")
|
112
|
+
users = fields.List(fields.Nested(UserSchema), required=True, description="The list of users")
|
113
|
+
admin_users = fields.List(fields.Nested(AdminSchema), required=True, description="The list of admin users")
|
114
|
+
|
115
|
+
# endregion
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from flask.views import MethodView
|
2
|
+
from flask import current_app as app
|
3
|
+
from loguru import logger
|
4
|
+
|
5
|
+
from hiddifypanel.models import Child, Role
|
6
|
+
from hiddifypanel.auth import login_required
|
7
|
+
|
8
|
+
from .schema import ChildStatusInputSchema, ChildStatusOutputSchema
|
9
|
+
|
10
|
+
|
11
|
+
class StatusApi(MethodView):
|
12
|
+
decorators = [login_required(node_auth=True)]
|
13
|
+
|
14
|
+
@app.input(ChildStatusInputSchema, arg_name='data') # type: ignore
|
15
|
+
@app.output(ChildStatusOutputSchema) # type: ignore
|
16
|
+
def post(self, data):
|
17
|
+
logger.info(f"Checking the existence of child with unique_id: {data['child_unique_id']}")
|
18
|
+
res = ChildStatusOutputSchema()
|
19
|
+
res.existance = False # type: ignore
|
20
|
+
|
21
|
+
child = Child.query.filter(Child.unique_id == data['child_unique_id']).first()
|
22
|
+
if child:
|
23
|
+
logger.info(f"Child with unique_id: {data['child_unique_id']} exists")
|
24
|
+
res.existance = True # type: ignore
|
25
|
+
|
26
|
+
return res
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
from flask.views import MethodView
|
3
|
+
from flask import current_app as app
|
4
|
+
from flask import g
|
5
|
+
from apiflask import abort
|
6
|
+
|
7
|
+
from hiddifypanel.models.user import User
|
8
|
+
from hiddifypanel.database import db
|
9
|
+
from hiddifypanel.models.child import Child
|
10
|
+
from loguru import logger
|
11
|
+
from hiddifypanel.models import *
|
12
|
+
from hiddifypanel.auth import login_required
|
13
|
+
from .schema import SyncInputSchema, SyncOutputSchema
|
14
|
+
|
15
|
+
|
16
|
+
class SyncApi(MethodView):
|
17
|
+
decorators = [login_required(node_auth=True)]
|
18
|
+
|
19
|
+
@app.input(SyncInputSchema, arg_name='data') # type: ignore
|
20
|
+
@app.output(SyncOutputSchema) # type: ignore
|
21
|
+
def put(self, data):
|
22
|
+
from hiddifypanel import hutils
|
23
|
+
unique_id = Child.node.unique_id
|
24
|
+
|
25
|
+
logger.info(f"Sync child with unique_id: {unique_id}")
|
26
|
+
if not hutils.node.is_parent():
|
27
|
+
logger.error("Not a parent")
|
28
|
+
abort(400, "Not a parent")
|
29
|
+
|
30
|
+
child = Child.query.filter(Child.unique_id == unique_id).first()
|
31
|
+
if not child:
|
32
|
+
logger.error("The child does not exist")
|
33
|
+
abort(404, "The child does not exist")
|
34
|
+
|
35
|
+
try:
|
36
|
+
logger.info("Syncing domains...")
|
37
|
+
bulk_register_domains(data['domains'], commit=False, force_child_unique_id=child.unique_id)
|
38
|
+
logger.info("Syncing hconfigs...")
|
39
|
+
bulk_register_configs(data['hconfigs'], commit=False, froce_child_unique_id=child.unique_id)
|
40
|
+
logger.info("Syncing proxies...")
|
41
|
+
Proxy.bulk_register(data['proxies'], commit=False, force_child_unique_id=child.unique_id)
|
42
|
+
db.session.commit() # type: ignore
|
43
|
+
except Exception as err:
|
44
|
+
with logger.contextualize(error=err):
|
45
|
+
logger.error(f"Error while syncing data")
|
46
|
+
abort(400, str(err))
|
47
|
+
|
48
|
+
res = SyncOutputSchema()
|
49
|
+
res.users = [u.to_schema() for u in User.query.all()] # type: ignore
|
50
|
+
res.admin_users = [a.to_schema() for a in AdminUser.query.all()] # type: ignore
|
51
|
+
|
52
|
+
logger.info("Returning sync output")
|
53
|
+
return res
|
@@ -0,0 +1,57 @@
|
|
1
|
+
from apiflask import abort
|
2
|
+
from flask.views import MethodView
|
3
|
+
from flask import current_app as app
|
4
|
+
from flask import g
|
5
|
+
from loguru import logger
|
6
|
+
|
7
|
+
from hiddifypanel.models import Child
|
8
|
+
from hiddifypanel.panel.usage import add_users_usage_uuid
|
9
|
+
from hiddifypanel.auth import login_required
|
10
|
+
|
11
|
+
from .schema import UsageInputOutputSchema
|
12
|
+
|
13
|
+
|
14
|
+
class UsageApi(MethodView):
|
15
|
+
decorators = [login_required(node_auth=True)]
|
16
|
+
|
17
|
+
@app.input(UsageInputOutputSchema, arg_name='data') # type: ignore
|
18
|
+
@app.output(UsageInputOutputSchema) # type: ignore
|
19
|
+
def put(self, data):
|
20
|
+
from hiddifypanel import hutils
|
21
|
+
child = Child.query.filter(Child.unique_id == Child.node.unique_id).first()
|
22
|
+
if not child:
|
23
|
+
logger.error("The child does not exist")
|
24
|
+
abort(400, "The child does not exist")
|
25
|
+
|
26
|
+
# parse request data
|
27
|
+
logger.debug(f"Received Usage data from child: {data}")
|
28
|
+
child_usages_data = hutils.node.convert_usage_api_response_to_dict(data)
|
29
|
+
|
30
|
+
# get current usage
|
31
|
+
logger.debug("Getting current usage data from parent")
|
32
|
+
parent_current_usages_data = hutils.node.convert_usage_api_response_to_dict(UsageInputOutputSchema().dump(hutils.node.get_users_usage_data_for_api())) # type: ignore
|
33
|
+
|
34
|
+
# calculate usages
|
35
|
+
logger.debug("Calculating increased usages")
|
36
|
+
increased_usages = self.__calculate_parent_increased_usages(child_usages_data, parent_current_usages_data)
|
37
|
+
logger.debug(f"Increased usages: {increased_usages}")
|
38
|
+
|
39
|
+
# add users usage
|
40
|
+
if increased_usages:
|
41
|
+
logger.info(f"Adding increased usages to parent: {increased_usages}")
|
42
|
+
add_users_usage_uuid(increased_usages, child.id)
|
43
|
+
|
44
|
+
return hutils.node.get_users_usage_data_for_api()
|
45
|
+
|
46
|
+
def __calculate_parent_increased_usages(self, child_usages_data: dict, parent_usages_data: dict) -> dict:
|
47
|
+
res = {}
|
48
|
+
for p_uuid, p_usage in parent_usages_data.items():
|
49
|
+
if child_usage := child_usages_data.get(p_uuid):
|
50
|
+
if child_usage['usage'] > 0:
|
51
|
+
usage_data = {
|
52
|
+
'usage': child_usage['usage'] - p_usage['usage'],
|
53
|
+
'devices': child_usage['devices'],
|
54
|
+
}
|
55
|
+
if usage_data['usage'] > 0:
|
56
|
+
res[p_uuid] = usage_data
|
57
|
+
return res
|
@@ -138,8 +138,7 @@ def create_package(call): # <- passes a CallbackQuery type object to your funct
|
|
138
138
|
domain = int(splt[4])
|
139
139
|
new_text = _("Please Wait...")
|
140
140
|
bot.edit_message_text(new_text, call.message.chat.id, call.message.message_id, reply_markup=None)
|
141
|
-
|
142
|
-
domain = DT.query.filter(DT.id == domain).first()
|
141
|
+
domain = Domain.query.filter(Domain.id == domain).first()
|
143
142
|
from . import Usage
|
144
143
|
admin_id = admin.id
|
145
144
|
admin_name = admin.name
|
hiddifypanel/panel/common.py
CHANGED
@@ -18,6 +18,8 @@ def init_app(app: APIFlask):
|
|
18
18
|
app.jinja_env.globals['UserMode'] = UserMode
|
19
19
|
app.jinja_env.globals['hconfig'] = hconfig
|
20
20
|
app.jinja_env.globals['g'] = g
|
21
|
+
app.jinja_env.globals['hutils'] = hutils
|
22
|
+
app.jinja_env.globals['hiddify'] = hiddify
|
21
23
|
app.jinja_env.globals['version'] = hiddifypanel.__version__
|
22
24
|
app.jinja_env.globals['static_url_for'] = hutils.flask.static_url_for
|
23
25
|
app.jinja_env.globals['hurl_for'] = hutils.flask.hurl_for
|
@@ -34,6 +36,12 @@ def init_app(app: APIFlask):
|
|
34
36
|
|
35
37
|
@app.errorhandler(Exception)
|
36
38
|
def internal_server_error(e):
|
39
|
+
if isinstance(e, Exception):
|
40
|
+
if hutils.flask.is_api_call(request.path):
|
41
|
+
return {
|
42
|
+
'msg': str(e),
|
43
|
+
}, 500
|
44
|
+
|
37
45
|
if hasattr(e, 'code') and e.code == 404:
|
38
46
|
return jsonify({
|
39
47
|
'message': 'Not Found',
|
@@ -68,6 +76,7 @@ def init_app(app: APIFlask):
|
|
68
76
|
# print(request.headers)
|
69
77
|
if not request.accept_mimetypes.accept_html:
|
70
78
|
return app.error_callback(e)
|
79
|
+
# if it's interval server error
|
71
80
|
if e.status_code == 500:
|
72
81
|
trace = traceback.format_exc()
|
73
82
|
|
@@ -81,9 +90,17 @@ def init_app(app: APIFlask):
|
|
81
90
|
has_update = "dev" not in hiddifypanel.__version__ and f'{last_version}' != hiddifypanel.__version__
|
82
91
|
|
83
92
|
return render_template('500.html', error=e, trace=trace, has_update=has_update, last_version=last_version, issue_link=issue_link), 500
|
93
|
+
|
94
|
+
# if it's access denied error
|
84
95
|
# if e.status_code in [400,401,403]:
|
85
96
|
# return render_template('access-denied.html',error=e), e.status_code
|
86
97
|
|
98
|
+
# if it's api error
|
99
|
+
if hutils.flask.is_api_call(request.path):
|
100
|
+
return {
|
101
|
+
'msg': e.message,
|
102
|
+
}, e.status_code
|
103
|
+
|
87
104
|
return render_template('error.html', error=e), e.status_code
|
88
105
|
|
89
106
|
@app.url_defaults
|
@@ -133,12 +150,6 @@ def init_app(app: APIFlask):
|
|
133
150
|
g.child = Child.by_id(g.__child_id) or abort(404, "Child not found")
|
134
151
|
g.account = current_account
|
135
152
|
|
136
|
-
@app.before_first_request
|
137
|
-
def first_request():
|
138
|
-
import hiddifypanel.panel.commercial.telegrambot as telegrambot
|
139
|
-
if (not telegrambot.bot) or (not telegrambot.bot.username): # type: ignore
|
140
|
-
telegrambot.register_bot(set_hook=True)
|
141
|
-
|
142
153
|
@app.before_request
|
143
154
|
def base_middleware():
|
144
155
|
if request.endpoint == 'static' or request.endpoint == "videos":
|
@@ -174,3 +185,7 @@ def init_app(app: APIFlask):
|
|
174
185
|
return auth_before
|
175
186
|
|
176
187
|
app.jinja_env.globals['generate_github_issue_link_for_admin_sidebar'] = hutils.github_issue.generate_github_issue_link_for_admin_sidebar
|
188
|
+
with app.app_context():
|
189
|
+
import hiddifypanel.panel.commercial.telegrambot as telegrambot
|
190
|
+
if (not telegrambot.bot) or (not telegrambot.bot.username): # type: ignore
|
191
|
+
telegrambot.register_bot(set_hook=True)
|
hiddifypanel/panel/hiddify.py
CHANGED
@@ -1,22 +1,16 @@
|
|
1
|
-
import glob
|
2
1
|
import re
|
3
|
-
import json
|
4
2
|
import subprocess
|
5
3
|
|
6
4
|
from datetime import datetime
|
7
5
|
from typing import Tuple
|
8
|
-
from cryptography.hazmat.primitives import serialization
|
9
|
-
from cryptography.hazmat.primitives.asymmetric import x25519
|
10
6
|
from flask import current_app, g
|
11
7
|
from flask_babel import lazy_gettext as _
|
12
|
-
from flask_babel import gettext as __
|
13
8
|
from datetime import timedelta
|
14
9
|
|
15
10
|
from hiddifypanel.cache import cache
|
16
11
|
from hiddifypanel.models import *
|
17
12
|
from hiddifypanel.database import db
|
18
13
|
from hiddifypanel.hutils.utils import *
|
19
|
-
from hiddifypanel.Events import domain_changed
|
20
14
|
from hiddifypanel import hutils
|
21
15
|
from hiddifypanel.panel.run_commander import commander, Command
|
22
16
|
import subprocess
|
@@ -69,23 +63,9 @@ def exec_command(cmd, cwd=None):
|
|
69
63
|
|
70
64
|
|
71
65
|
def quick_apply_users():
|
72
|
-
if hconfig(ConfigEnum.is_parent):
|
73
|
-
return
|
74
|
-
# from hiddifypanel.panel import usage
|
75
|
-
# usage.update_local_usage()
|
76
|
-
# return
|
77
|
-
# for user in User.query.all():
|
78
|
-
# if user.is_active:
|
79
|
-
# xray_api.add_client(user.uuid)
|
80
|
-
# else:
|
81
|
-
# xray_api.remove_client(user.uuid)
|
82
|
-
|
83
|
-
# exec_command("sudo /opt/hiddify-manager/install.sh apply_users --no-gui")
|
84
|
-
|
85
66
|
# run install.sh apply_users
|
86
67
|
commander(Command.apply_users)
|
87
68
|
|
88
|
-
# time.sleep(1)
|
89
69
|
return {"status": 'success'}
|
90
70
|
|
91
71
|
|
@@ -145,11 +125,12 @@ def check_need_reset(old_configs, do=False):
|
|
145
125
|
|
146
126
|
|
147
127
|
def get_child(unique_id):
|
148
|
-
child_id = Child.current.id
|
128
|
+
child_id = Child.current().id
|
149
129
|
if unique_id is None or unique_id in ["self", "default", str(hconfig(ConfigEnum.unique_id))]:
|
150
130
|
child_id = 0
|
151
131
|
else:
|
152
132
|
child = Child.query.filter(Child.unique_id == str(unique_id)).first()
|
133
|
+
# TODO: this doesn't work because name and mode fields are nullable
|
153
134
|
if not child:
|
154
135
|
child = Child(unique_id=str(unique_id))
|
155
136
|
db.session.add(child)
|
@@ -164,7 +145,7 @@ def dump_db_to_dict():
|
|
164
145
|
"users": [u.to_dict() for u in User.query.all()],
|
165
146
|
"domains": [u.to_dict() for u in Domain.query.all()],
|
166
147
|
"proxies": [u.to_dict() for u in Proxy.query.all()],
|
167
|
-
"parent_domains": [] if not hconfig(ConfigEnum.license) else [u.to_dict() for u in ParentDomain.query.all()],
|
148
|
+
# "parent_domains": [] if not hconfig(ConfigEnum.license) else [u.to_dict() for u in ParentDomain.query.all()],
|
168
149
|
'admin_users': [d.to_dict() for d in AdminUser.query.all()],
|
169
150
|
"hconfigs": [*[u.to_dict() for u in BoolConfig.query.all()],
|
170
151
|
*[u.to_dict() for u in StrConfig.query.all()]]
|
@@ -195,7 +176,7 @@ def set_db_from_json(json_data, override_child_unique_id=True, set_users=True, s
|
|
195
176
|
# override root child unique id
|
196
177
|
if override_child_unique_id:
|
197
178
|
backup_child_unique_id = get_backup_child_unique_id(json_data)
|
198
|
-
replace_backup_child_unique_id(json_data, backup_child_unique_id, Child.current.unique_id)
|
179
|
+
replace_backup_child_unique_id(json_data, backup_child_unique_id, Child.current().unique_id)
|
199
180
|
|
200
181
|
# restore childs
|
201
182
|
if set_child and 'childs' in json_data:
|
@@ -243,13 +224,12 @@ def set_db_from_json(json_data, override_child_unique_id=True, set_users=True, s
|
|
243
224
|
if set_users and 'users' in json_data:
|
244
225
|
User.bulk_register(json_data['users'], commit=False, remove=remove_users)
|
245
226
|
if set_domains and 'domains' in json_data:
|
246
|
-
bulk_register_domains(json_data['domains'], commit=False, remove=remove_domains
|
247
|
-
|
248
|
-
# ParentDomain.bulk_register(json_data['parent_domains'], commit=False, remove=remove_domains)
|
227
|
+
bulk_register_domains(json_data['domains'], commit=False, remove=remove_domains)
|
228
|
+
|
249
229
|
if set_settings and 'hconfigs' in json_data:
|
250
|
-
bulk_register_configs(json_data["hconfigs"], commit=True,
|
230
|
+
bulk_register_configs(json_data["hconfigs"], commit=True, override_unique_id=override_unique_id)
|
251
231
|
if 'proxies' in json_data:
|
252
|
-
Proxy.bulk_register(json_data['proxies'], commit=False
|
232
|
+
Proxy.bulk_register(json_data['proxies'], commit=False)
|
253
233
|
|
254
234
|
ids_without_parent = get_ids_without_parent({u.id: u.to_dict() for u in AdminUser.query.all()})
|
255
235
|
owner = AdminUser.get_super_admin()
|
@@ -276,49 +256,13 @@ def get_domain_btn_link(domain):
|
|
276
256
|
return res
|
277
257
|
|
278
258
|
|
279
|
-
def debug_flash_if_not_in_the_same_asn(domain):
|
280
|
-
from hiddifypanel.hutils.network.auto_ip_selector import IPASN
|
281
|
-
ipv4 = hutils.network.get_ip_str(4)
|
282
|
-
dip = hutils.network.get_domain_ip(domain)
|
283
|
-
try:
|
284
|
-
if IPASN:
|
285
|
-
asn_ipv4 = IPASN.get(ipv4)
|
286
|
-
asn_dip = IPASN.get(dip)
|
287
|
-
# country_ipv4= ipcountry.get(ipv4)
|
288
|
-
# country_dip= ipcountry.get(dip)
|
289
|
-
if asn_ipv4.get('autonomous_system_organization') != asn_dip.get('autonomous_system_organization'):
|
290
|
-
hutils.flask.flash(_("selected domain for REALITY is not in the same ASN. To better use of the protocol, it is better to find a domain in the same ASN.") +
|
291
|
-
f"<br> Server ASN={asn_ipv4.get('autonomous_system_organization','unknown')}<br>{domain}_ASN={asn_dip.get('autonomous_system_organization','unknown')}", "warning")
|
292
|
-
except BaseException:
|
293
|
-
pass
|
294
|
-
|
295
|
-
|
296
|
-
def generate_x25519_keys():
|
297
|
-
priv = x25519.X25519PrivateKey.generate()
|
298
|
-
pub = priv.public_key()
|
299
|
-
priv_bytes = priv.private_bytes(
|
300
|
-
encoding=serialization.Encoding.Raw,
|
301
|
-
format=serialization.PrivateFormat.Raw,
|
302
|
-
encryption_algorithm=serialization.NoEncryption()
|
303
|
-
)
|
304
|
-
pub_bytes = pub.public_bytes(
|
305
|
-
encoding=serialization.Encoding.Raw,
|
306
|
-
format=serialization.PublicFormat.Raw
|
307
|
-
)
|
308
|
-
import base64
|
309
|
-
pub_str = base64.urlsafe_b64encode(pub_bytes).decode()[:-1]
|
310
|
-
priv_str = base64.urlsafe_b64encode(priv_bytes).decode()[:-1]
|
311
|
-
|
312
|
-
return {'private_key': priv_str, 'public_key': pub_str}
|
313
|
-
|
314
|
-
|
315
259
|
def get_ssh_client_version(user):
|
316
260
|
return 'SSH-2.0-OpenSSH_7.4p1'
|
317
261
|
|
318
262
|
|
319
263
|
def get_account_panel_link(account: BaseAccount, host: str, is_https: bool = True, prefere_path_only: bool = False, child_id=None):
|
320
264
|
if child_id is None:
|
321
|
-
child_id = Child.current.id
|
265
|
+
child_id = Child.current().id
|
322
266
|
is_admin = isinstance(account, AdminUser)
|
323
267
|
basic_auth = False # is_admin #because safri does not support it.
|
324
268
|
|
@@ -363,11 +307,8 @@ def clone_model(model):
|
|
363
307
|
for k in table.columns.keys():
|
364
308
|
if k == "id":
|
365
309
|
continue
|
366
|
-
# if k in table.primary_key:
|
367
|
-
# continue
|
368
310
|
setattr(new_model, f'{k}', getattr(model, k))
|
369
311
|
|
370
|
-
# data.pop('id')
|
371
312
|
return new_model
|
372
313
|
|
373
314
|
|
@@ -392,18 +333,6 @@ def get_backup_child_unique_id(backupdata: dict) -> str:
|
|
392
333
|
return "self"
|
393
334
|
return backupdata['childs'][0]['unique_id']
|
394
335
|
|
395
|
-
# for k, v in backupdata.items():
|
396
|
-
# if k == 'admin_users' or k == 'users':
|
397
|
-
# continue
|
398
|
-
# if k == 'childs':
|
399
|
-
# if len(v) < 1:
|
400
|
-
# continue
|
401
|
-
# return v[0]['unique_id']
|
402
|
-
# else:
|
403
|
-
# for item in v:
|
404
|
-
# return item['child_unique_id']
|
405
|
-
# return 'self'
|
406
|
-
|
407
336
|
|
408
337
|
def is_hiddify_next_version(major_v: int = 0, minor_v: int = 0, patch_v: int = 0) -> bool:
|
409
338
|
'''If the user agent version be equals or higher than parameters returns True'''
|