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
@@ -11,13 +11,13 @@ from hiddifypanel.models import AdminUser
|
|
11
11
|
class AdminUsersApi(MethodView):
|
12
12
|
decorators = [login_required({Role.super_admin, Role.admin})]
|
13
13
|
|
14
|
-
@app.output(AdminSchema(many=True))
|
14
|
+
@app.output(AdminSchema(many=True)) # type: ignore
|
15
15
|
def get(self):
|
16
16
|
admins = AdminUser.query.filter(AdminUser.id.in_(g.account.recursive_sub_admins_ids())).all() or abort(404, "You have no admin")
|
17
17
|
return [admin.to_dict() for admin in admins] # type: ignore
|
18
18
|
|
19
|
-
@app.input(AdminSchema, arg_name='data')
|
20
|
-
@app.output(AdminSchema)
|
19
|
+
@app.input(AdminSchema, arg_name='data') # type: ignore
|
20
|
+
@app.output(AdminSchema) # type: ignore
|
21
21
|
def put(self, data):
|
22
22
|
uuid = data.get('uuid') or abort(422, "Parameter issue: 'uuid'")
|
23
23
|
admin = AdminUser.by_uuid(uuid) # type: ignore
|
@@ -31,4 +31,4 @@ class AdminUsersApi(MethodView):
|
|
31
31
|
abort(403, "You don't have permission to access this admin")
|
32
32
|
|
33
33
|
dbadmin = AdminUser.add_or_update(**data) or abort(502, "Unknown issue: Admin is not added")
|
34
|
-
return dbadmin.to_dict()
|
34
|
+
return dbadmin.to_dict() # type: ignore
|
@@ -0,0 +1,157 @@
|
|
1
|
+
import uuid
|
2
|
+
from apiflask.fields import String, Float, Enum, Date, Integer, Boolean
|
3
|
+
from apiflask import Schema, fields
|
4
|
+
from typing import Any, Mapping
|
5
|
+
|
6
|
+
from hiddifypanel.models import UserMode, Lang, AdminMode
|
7
|
+
from hiddifypanel import hutils
|
8
|
+
|
9
|
+
# region user api
|
10
|
+
|
11
|
+
|
12
|
+
class FriendlyDateTime(fields.Field):
|
13
|
+
def _serialize(self, value: Any, attr: str | None, obj: Any, **kwargs):
|
14
|
+
return hutils.convert.time_to_json(value)
|
15
|
+
|
16
|
+
def _deserialize(self, value: Any, attr: str | None, data: Mapping[str, Any] | None, **kwargs):
|
17
|
+
return hutils.convert.json_to_time(value)
|
18
|
+
|
19
|
+
|
20
|
+
class FriendlyUUID(fields.Field):
|
21
|
+
def _serialize(self, value: Any, attr: str | None, obj: Any, **kwargs):
|
22
|
+
if value is None:
|
23
|
+
return None
|
24
|
+
return str(value)
|
25
|
+
|
26
|
+
def _deserialize(self, value: Any, attr: str | None, data: Mapping[str, Any] | None, **kwargs):
|
27
|
+
if value is None:
|
28
|
+
return None
|
29
|
+
try:
|
30
|
+
return uuid.UUID(value)
|
31
|
+
except ValueError:
|
32
|
+
self.fail('Invalid uuid')
|
33
|
+
|
34
|
+
|
35
|
+
class UserSchema(Schema):
|
36
|
+
uuid = FriendlyUUID(required=True, description="Unique identifier for the user")
|
37
|
+
name = String(required=True, description="Name of the user")
|
38
|
+
|
39
|
+
usage_limit_GB = Float(
|
40
|
+
required=False,
|
41
|
+
allow_none=True,
|
42
|
+
description="The data usage limit for the user in gigabytes"
|
43
|
+
)
|
44
|
+
package_days = Integer(
|
45
|
+
required=False,
|
46
|
+
allow_none=True,
|
47
|
+
description="The number of days in the user's package"
|
48
|
+
)
|
49
|
+
mode = Enum(UserMode,
|
50
|
+
required=False,
|
51
|
+
allow_none=True,
|
52
|
+
description="The mode of the user's account, which dictates access level or type"
|
53
|
+
)
|
54
|
+
last_online = FriendlyDateTime(
|
55
|
+
format="%Y-%m-%d %H:%M:%S",
|
56
|
+
allow_none=True,
|
57
|
+
description="The last time the user was online, converted to a JSON-friendly format"
|
58
|
+
)
|
59
|
+
start_date = Date(
|
60
|
+
format='%Y-%m-%d',
|
61
|
+
allow_none=True,
|
62
|
+
description="The start date of the user's package, in a JSON-friendly format"
|
63
|
+
)
|
64
|
+
current_usage_GB = Float(
|
65
|
+
required=False,
|
66
|
+
allow_none=True,
|
67
|
+
description="The current data usage of the user in gigabytes"
|
68
|
+
)
|
69
|
+
last_reset_time = Date(
|
70
|
+
format='%Y-%m-%d',
|
71
|
+
description="The last time the user's data usage was reset, in a JSON-friendly format",
|
72
|
+
allow_none=True
|
73
|
+
)
|
74
|
+
comment = String(
|
75
|
+
missing=None,
|
76
|
+
allow_none=True,
|
77
|
+
description="An optional comment about the user"
|
78
|
+
)
|
79
|
+
added_by_uuid = FriendlyUUID(
|
80
|
+
required=False,
|
81
|
+
description="UUID of the admin who added this user",
|
82
|
+
allow_none=True,
|
83
|
+
# validate=OneOf([p.uuid for p in AdminUser.query.all()])
|
84
|
+
)
|
85
|
+
telegram_id = Integer(
|
86
|
+
required=False,
|
87
|
+
description="The Telegram ID associated with the user",
|
88
|
+
allow_none=True
|
89
|
+
)
|
90
|
+
ed25519_private_key = String(
|
91
|
+
required=False,
|
92
|
+
allow_none=True,
|
93
|
+
description="If empty, it will be created automatically, The user's private key using the Ed25519 algorithm"
|
94
|
+
)
|
95
|
+
ed25519_public_key = String(
|
96
|
+
required=False,
|
97
|
+
allow_none=True,
|
98
|
+
description="If empty, it will be created automatically,The user's public key using the Ed25519 algorithm"
|
99
|
+
)
|
100
|
+
wg_pk = String(
|
101
|
+
required=False,
|
102
|
+
allow_none=True,
|
103
|
+
description="If empty, it will be created automatically, The user's WireGuard private key"
|
104
|
+
)
|
105
|
+
|
106
|
+
wg_pub = String(
|
107
|
+
required=False,
|
108
|
+
allow_none=True,
|
109
|
+
description="If empty, it will be created automatically, The user's WireGuard public key"
|
110
|
+
)
|
111
|
+
wg_psk = String(
|
112
|
+
required=False,
|
113
|
+
allow_none=True,
|
114
|
+
description="If empty, it will be created automatically, The user's WireGuard preshared key"
|
115
|
+
)
|
116
|
+
|
117
|
+
lang = Enum(Lang, required=False, allow_none=True, description="The language of the user")
|
118
|
+
|
119
|
+
|
120
|
+
class PatchUserSchema(UserSchema):
|
121
|
+
def __init__(self, *args, **kwargs):
|
122
|
+
super().__init__(*args, **kwargs)
|
123
|
+
self.fields['name'].required = False
|
124
|
+
pass
|
125
|
+
|
126
|
+
# endregion
|
127
|
+
|
128
|
+
# region admin api
|
129
|
+
|
130
|
+
|
131
|
+
class AdminSchema(Schema):
|
132
|
+
name = String(required=True, description='The name of the admin')
|
133
|
+
comment = String(required=False, description='A comment related to the admin', allow_none=True)
|
134
|
+
uuid = FriendlyUUID(required=True, description='The unique identifier for the admin')
|
135
|
+
mode = Enum(AdminMode, required=True, description='The mode for the admin')
|
136
|
+
can_add_admin = Boolean(required=True, description='Whether the admin can add other admins')
|
137
|
+
parent_admin_uuid = FriendlyUUID(description='The unique identifier for the parent admin', allow_none=True,
|
138
|
+
# validate=OneOf([p.uuid for p in AdminUser.query.all()])
|
139
|
+
)
|
140
|
+
telegram_id = Integer(required=False, description='The Telegram ID associated with the admin', allow_none=True)
|
141
|
+
lang = Enum(Lang, required=True)
|
142
|
+
|
143
|
+
|
144
|
+
class PatchAdminSchema(AdminSchema):
|
145
|
+
def __init__(self, *args, **kwargs):
|
146
|
+
super().__init__(*args, **kwargs)
|
147
|
+
self.fields['name'].required = False
|
148
|
+
self.fields['mode'].required = False
|
149
|
+
self.fields['lang'].required = False
|
150
|
+
self.fields['can_add_admin'].required = False
|
151
|
+
pass
|
152
|
+
# endregion
|
153
|
+
|
154
|
+
|
155
|
+
class SuccessfulSchema(Schema):
|
156
|
+
status = Integer()
|
157
|
+
msg = String()
|
@@ -10,7 +10,7 @@ from hiddifypanel.panel import hiddify
|
|
10
10
|
from hiddifypanel import hutils
|
11
11
|
|
12
12
|
|
13
|
-
class
|
13
|
+
class ServerStatusOutputSchema(Schema):
|
14
14
|
stats = Dict(required=True, description="System stats")
|
15
15
|
usage_history = Dict(required=True, description="System usage history")
|
16
16
|
|
@@ -18,9 +18,9 @@ class ServerStatus(Schema):
|
|
18
18
|
class AdminServerStatusApi(MethodView):
|
19
19
|
decorators = [login_required({Role.super_admin, Role.admin, Role.agent})]
|
20
20
|
|
21
|
-
@app.output(
|
21
|
+
@app.output(ServerStatusOutputSchema) # type: ignore
|
22
22
|
def get(self):
|
23
|
-
dto =
|
23
|
+
dto = ServerStatusOutputSchema()
|
24
24
|
dto.stats = { # type: ignore
|
25
25
|
'system': hutils.system.system_stats(),
|
26
26
|
'top5': hutils.system.top_processes()
|
@@ -1,94 +1,30 @@
|
|
1
1
|
from flask import g
|
2
2
|
from flask.views import MethodView
|
3
3
|
from flask import current_app as app
|
4
|
-
from apiflask import abort
|
4
|
+
from apiflask import abort
|
5
5
|
from hiddifypanel.auth import login_required
|
6
6
|
from hiddifypanel.models import *
|
7
7
|
from hiddifypanel.panel import hiddify
|
8
8
|
from hiddifypanel.drivers import user_driver
|
9
9
|
from hiddifypanel.panel import hiddify
|
10
|
-
from apiflask.fields import UUID, String, Float, Enum, Date, Time, Integer
|
11
10
|
|
12
|
-
from . import
|
13
|
-
|
14
|
-
|
15
|
-
class UserSchema(Schema):
|
16
|
-
uuid = UUID(required=True, description="Unique identifier for the user")
|
17
|
-
name = String(required=True, description="Name of the user")
|
18
|
-
|
19
|
-
usage_limit_GB = Float(
|
20
|
-
required=False,
|
21
|
-
description="The data usage limit for the user in gigabytes"
|
22
|
-
)
|
23
|
-
package_days = Integer(
|
24
|
-
required=False,
|
25
|
-
description="The number of days in the user's package"
|
26
|
-
)
|
27
|
-
mode = Enum(UserMode,
|
28
|
-
required=False,
|
29
|
-
description="The mode of the user's account, which dictates access level or type"
|
30
|
-
)
|
31
|
-
last_online = Time(
|
32
|
-
format="%Y-%m-%d %H:%M:%S",
|
33
|
-
description="The last time the user was online, converted to a JSON-friendly format"
|
34
|
-
)
|
35
|
-
start_date = Date(
|
36
|
-
format='%Y-%m-%d',
|
37
|
-
description="The start date of the user's package, in a JSON-friendly format"
|
38
|
-
)
|
39
|
-
current_usage_GB = Float(
|
40
|
-
required=False,
|
41
|
-
description="The current data usage of the user in gigabytes"
|
42
|
-
)
|
43
|
-
last_reset_time = Date(
|
44
|
-
format='%Y-%m-%d',
|
45
|
-
description="The last time the user's data usage was reset, in a JSON-friendly format"
|
46
|
-
)
|
47
|
-
comment = String(
|
48
|
-
missing=None,
|
49
|
-
description="An optional comment about the user"
|
50
|
-
)
|
51
|
-
added_by_uuid = UUID(
|
52
|
-
required=False,
|
53
|
-
description="UUID of the admin who added this user",
|
54
|
-
allow_none=True,
|
55
|
-
# validate=OneOf([p.uuid for p in AdminUser.query.all()])
|
56
|
-
)
|
57
|
-
telegram_id = Integer(
|
58
|
-
required=False,
|
59
|
-
description="The Telegram ID associated with the user",
|
60
|
-
allow_none=True
|
61
|
-
)
|
62
|
-
ed25519_private_key = String(
|
63
|
-
required=False,
|
64
|
-
description="If empty, it will be created automatically, The user's private key using the Ed25519 algorithm"
|
65
|
-
)
|
66
|
-
ed25519_public_key = String(
|
67
|
-
required=False,
|
68
|
-
description="If empty, it will be created automatically,The user's public key using the Ed25519 algorithm"
|
69
|
-
)
|
70
|
-
|
71
|
-
|
72
|
-
class PatchUserSchema(UserSchema):
|
73
|
-
def __init__(self, *args, **kwargs):
|
74
|
-
super().__init__(*args, **kwargs)
|
75
|
-
self.fields['name'].required = False
|
76
|
-
pass
|
11
|
+
from . import has_permission
|
12
|
+
from .schema import UserSchema, PatchUserSchema, SuccessfulSchema
|
77
13
|
|
78
14
|
|
79
15
|
class UserApi(MethodView):
|
80
16
|
decorators = [login_required({Role.super_admin, Role.admin, Role.agent})]
|
81
17
|
|
82
|
-
@app.output(UserSchema)
|
18
|
+
@app.output(UserSchema) # type: ignore
|
83
19
|
def get(self, uuid):
|
84
20
|
user = User.by_uuid(uuid) or abort(404, "user not found")
|
85
21
|
if not has_permission(user):
|
86
22
|
abort(403, "You don't have permission to access this user")
|
87
23
|
|
88
|
-
return user.to_dict(False)
|
24
|
+
return user.to_dict(False) # type: ignore
|
89
25
|
|
90
|
-
@app.input(PatchUserSchema, arg_name="data")
|
91
|
-
@app.output(SuccessfulSchema)
|
26
|
+
@app.input(PatchUserSchema, arg_name="data") # type: ignore
|
27
|
+
@app.output(SuccessfulSchema) # type: ignore
|
92
28
|
def patch(self, uuid, data):
|
93
29
|
user = User.by_uuid(uuid) or abort(404, "user not found")
|
94
30
|
if not has_permission(user):
|
@@ -104,11 +40,11 @@ class UserApi(MethodView):
|
|
104
40
|
hiddify.quick_apply_users()
|
105
41
|
return {'status': 200, 'msg': 'ok'}
|
106
42
|
|
107
|
-
@app.output(SuccessfulSchema)
|
43
|
+
@app.output(SuccessfulSchema) # type: ignore
|
108
44
|
def delete(self, uuid):
|
109
45
|
user = User.by_uuid(uuid) or abort(404, "user not found")
|
110
46
|
if not has_permission(user):
|
111
47
|
abort(403, "You don't have permission to access this user")
|
112
|
-
user.remove()
|
48
|
+
user.remove() # type: ignore
|
113
49
|
hiddify.quick_apply_users()
|
114
50
|
return {'status': 200, 'msg': 'ok'}
|
@@ -22,7 +22,7 @@ class UsersApi(MethodView):
|
|
22
22
|
@app.output(UserSchema) # type: ignore
|
23
23
|
def put(self, data):
|
24
24
|
uuid = data.get('uuid') or abort(422, "Parameter issue: 'uuid'")
|
25
|
-
user = User.by_uuid(uuid)
|
25
|
+
user = User.by_uuid(uuid) # type: ignore
|
26
26
|
|
27
27
|
if not data.get('added_by_uuid'):
|
28
28
|
data['added_by_uuid'] = g.account.uuid
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from apiflask import APIBlueprint
|
2
|
+
|
3
|
+
bp = APIBlueprint("api_child", __name__, url_prefix="/<proxy_path>/api/v2/child/", enable_openapi=True)
|
4
|
+
|
5
|
+
|
6
|
+
def init_app(app):
|
7
|
+
with app.app_context():
|
8
|
+
from .register_parent_api import RegisterWithParentApi
|
9
|
+
from .sync_parent_api import SyncWithParentApi
|
10
|
+
from .actions import ApplyConfig, Restart, Status, UpdateUsage, Install
|
11
|
+
bp.add_url_rule('/sync-parent/', view_func=SyncWithParentApi)
|
12
|
+
bp.add_url_rule('/register-parent/', view_func=RegisterWithParentApi)
|
13
|
+
bp.add_url_rule('/status/', view_func=Status)
|
14
|
+
bp.add_url_rule('/restart/', view_func=Restart)
|
15
|
+
bp.add_url_rule('/apply-config/', view_func=ApplyConfig)
|
16
|
+
bp.add_url_rule('/install/', view_func=Install)
|
17
|
+
bp.add_url_rule('/update-usage/', view_func=UpdateUsage)
|
18
|
+
app.register_blueprint(bp)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
from apiflask import fields, Schema
|
2
|
+
from flask import current_app as app
|
3
|
+
from flask import g
|
4
|
+
from flask.views import MethodView
|
5
|
+
from loguru import logger
|
6
|
+
|
7
|
+
from hiddifypanel.models.child import Child
|
8
|
+
from hiddifypanel.panel.run_commander import commander, Command
|
9
|
+
from hiddifypanel.auth import login_required
|
10
|
+
|
11
|
+
|
12
|
+
class Status(MethodView):
|
13
|
+
decorators = [login_required(node_auth=True)]
|
14
|
+
|
15
|
+
def post(self):
|
16
|
+
logger.info(f"Status action called by parent: {Child.node.unique_id}")
|
17
|
+
commander(Command.status)
|
18
|
+
return {'status': 200, 'msg': 'ok'}
|
19
|
+
|
20
|
+
|
21
|
+
class UpdateUsage(MethodView):
|
22
|
+
decorators = [login_required(node_auth=True)]
|
23
|
+
|
24
|
+
def post(self):
|
25
|
+
logger.info(f"Update usage action called by parent: {Child.node.unique_id}")
|
26
|
+
commander(Command.update_usage)
|
27
|
+
return {'status': 200, 'msg': 'ok'}
|
28
|
+
|
29
|
+
|
30
|
+
class Restart(MethodView):
|
31
|
+
decorators = [login_required(node_auth=True)]
|
32
|
+
|
33
|
+
def post(self):
|
34
|
+
logger.info(f"Restart action called by parent: {Child.node.unique_id}")
|
35
|
+
commander(Command.restart_services)
|
36
|
+
return {'status': 200, 'msg': 'ok'}
|
37
|
+
|
38
|
+
|
39
|
+
class ApplyConfig(MethodView):
|
40
|
+
decorators = [login_required(node_auth=True)]
|
41
|
+
|
42
|
+
def post(self):
|
43
|
+
logger.info(f"Apply config action called by parent: {Child.node.unique_id}")
|
44
|
+
commander(Command.apply)
|
45
|
+
return {'status': 200, 'msg': 'ok'}
|
46
|
+
|
47
|
+
|
48
|
+
class InstallSchema(Schema):
|
49
|
+
full = fields.Boolean(description="full install", required=True, default=True)
|
50
|
+
|
51
|
+
|
52
|
+
class Install(MethodView):
|
53
|
+
decorators = [login_required(node_auth=True)]
|
54
|
+
|
55
|
+
@app.input(InstallSchema, arg_name="data") # type: ignore
|
56
|
+
def post(self, data: dict):
|
57
|
+
if data.get('full'):
|
58
|
+
logger.info(f"Install action called by parent: {Child.node.unique_id}, full=True")
|
59
|
+
commander(Command.install)
|
60
|
+
else:
|
61
|
+
logger.info(f"Install action called by parent: {Child.node.unique_id}, full=False")
|
62
|
+
commander(Command.apply)
|
63
|
+
return {'status': 200, 'msg': 'ok'}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from apiflask import abort, fields, Schema
|
2
|
+
from flask.views import MethodView
|
3
|
+
from flask import current_app as app, g
|
4
|
+
from flask_babel import lazy_gettext as _
|
5
|
+
from loguru import logger
|
6
|
+
|
7
|
+
from hiddifypanel.auth import login_required
|
8
|
+
from hiddifypanel.models import set_hconfig, ConfigEnum, PanelMode, Role
|
9
|
+
from hiddifypanel import hutils
|
10
|
+
|
11
|
+
from .schema import RegisterWithParentInputSchema
|
12
|
+
|
13
|
+
|
14
|
+
class RegisterWithParentApi(MethodView):
|
15
|
+
decorators = [login_required({Role.super_admin})]
|
16
|
+
|
17
|
+
# TODO: incomplete (not used)
|
18
|
+
@app.input(RegisterWithParentInputSchema, arg_name='data') # type: ignore
|
19
|
+
def post(self, data):
|
20
|
+
logger.info(f"Registering panel with parent called by {data['parent_unique_id']}")
|
21
|
+
if hutils.node.is_parent() or hutils.node.is_child():
|
22
|
+
logger.error("The panel is not in standalone mode nor in child")
|
23
|
+
abort(400, 'The panel is not in standalone mode nor in child')
|
24
|
+
|
25
|
+
set_hconfig(ConfigEnum.parent_panel, data['parent_panel']) # type: ignore
|
26
|
+
|
27
|
+
if not hutils.node.child.register_to_parent(data['name'], data['apikey']):
|
28
|
+
logger.error("Child registration to parent failed")
|
29
|
+
set_hconfig(ConfigEnum.parent_panel, '') # type: ignore
|
30
|
+
abort(400, _('child.register-failed')) # type: ignore
|
31
|
+
|
32
|
+
set_hconfig(ConfigEnum.panel_mode, PanelMode.child) # type: ignore
|
33
|
+
logger.info("Registered panel with parent, panel mode is now child")
|
34
|
+
return {'status': 200, 'msg': 'ok'}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
from apiflask import Schema, fields
|
2
|
+
|
3
|
+
|
4
|
+
class RegisterWithParentInputSchema(Schema):
|
5
|
+
parent_panel = fields.String(required=True, description="The parent panel url")
|
6
|
+
name = fields.String(required=True, description="The child's name in the parent panel")
|
7
|
+
apikey = fields.String(description="The parent's apikey")
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from apiflask import abort
|
2
|
+
from flask.views import MethodView
|
3
|
+
from flask_babel import lazy_gettext as _
|
4
|
+
from flask import g
|
5
|
+
from loguru import logger
|
6
|
+
|
7
|
+
from hiddifypanel.models.child import Child
|
8
|
+
from hiddifypanel.auth import login_required
|
9
|
+
from hiddifypanel import hutils
|
10
|
+
|
11
|
+
|
12
|
+
class SyncWithParentApi(MethodView):
|
13
|
+
decorators = [login_required(node_auth=True)]
|
14
|
+
|
15
|
+
def post(self):
|
16
|
+
logger.info(f"Syncing panel with parent called by {Child.node.unique_id}")
|
17
|
+
if not hutils.node.child.sync_with_parent():
|
18
|
+
logger.error("Sync with parent failed")
|
19
|
+
abort(400, _('child.sync-failed')) # type: ignore
|
20
|
+
logger.success(f"Synced panel with parent {Child.node.unique_id}")
|
21
|
+
return {'status': 200, 'msg': 'ok'}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from apiflask import APIBlueprint
|
2
|
+
|
3
|
+
bp = APIBlueprint("api_panel", __name__, url_prefix="/<proxy_path>/api/v2/panel/", enable_openapi=True)
|
4
|
+
|
5
|
+
|
6
|
+
def init_app(app):
|
7
|
+
|
8
|
+
with app.app_context():
|
9
|
+
from .info import PanelInfoApi
|
10
|
+
from .ping_pong import PingPongApi
|
11
|
+
bp.add_url_rule('/info/', view_func=PanelInfoApi)
|
12
|
+
bp.add_url_rule('/ping/', view_func=PingPongApi)
|
13
|
+
app.register_blueprint(bp)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from flask.views import MethodView
|
2
|
+
from flask import current_app as app
|
3
|
+
|
4
|
+
from hiddifypanel.auth import login_required
|
5
|
+
from hiddifypanel.models import Role
|
6
|
+
from hiddifypanel import __version__
|
7
|
+
|
8
|
+
from .schema import PanelInfoOutputSchema
|
9
|
+
|
10
|
+
|
11
|
+
class PanelInfoApi(MethodView):
|
12
|
+
decorators = [login_required(roles={Role.super_admin, Role.admin, Role.agent}, node_auth=True)]
|
13
|
+
|
14
|
+
@app.output(PanelInfoOutputSchema) # type: ignore
|
15
|
+
def get(self):
|
16
|
+
res = PanelInfoOutputSchema()
|
17
|
+
res.version = __version__ # type: ignore
|
18
|
+
return res
|
@@ -0,0 +1,23 @@
|
|
1
|
+
from flask.views import MethodView
|
2
|
+
|
3
|
+
from hiddifypanel.auth import login_required
|
4
|
+
from hiddifypanel.models import Role
|
5
|
+
|
6
|
+
|
7
|
+
class PingPongApi(MethodView):
|
8
|
+
decorators = [login_required(roles={Role.super_admin, Role.admin, Role.agent}, node_auth=True)]
|
9
|
+
|
10
|
+
def get(self):
|
11
|
+
return {'msg': 'GET: PONG'}
|
12
|
+
|
13
|
+
def post(self):
|
14
|
+
return {'msg': 'POST: PONG'}
|
15
|
+
|
16
|
+
def patch(self):
|
17
|
+
return {'msg': 'PATCH: PONG'}
|
18
|
+
|
19
|
+
def delete(self):
|
20
|
+
return {'msg': 'DELETE: PONG'}
|
21
|
+
|
22
|
+
def put(self):
|
23
|
+
return {'msg': 'PUT: PONG'}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from apiflask import APIBlueprint
|
2
|
+
|
3
|
+
bp = APIBlueprint("api_parent", __name__, url_prefix="/<proxy_path>/api/v2/parent/", enable_openapi=True)
|
4
|
+
|
5
|
+
|
6
|
+
def init_app(app):
|
7
|
+
with app.app_context():
|
8
|
+
from .register_api import RegisterApi
|
9
|
+
from .sync_api import SyncApi
|
10
|
+
from .usage_api import UsageApi
|
11
|
+
from .status_api import StatusApi
|
12
|
+
bp.add_url_rule('/status/', view_func=StatusApi)
|
13
|
+
bp.add_url_rule('/register/', view_func=RegisterApi)
|
14
|
+
bp.add_url_rule('/sync/', view_func=SyncApi)
|
15
|
+
bp.add_url_rule('/usage/', view_func=UsageApi)
|
16
|
+
app.register_blueprint(bp)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
from apiflask import abort
|
2
|
+
from hiddifypanel.database import db
|
3
|
+
from flask import current_app as app
|
4
|
+
from flask.views import MethodView
|
5
|
+
from loguru import logger
|
6
|
+
|
7
|
+
from hiddifypanel.models import *
|
8
|
+
from hiddifypanel.auth import login_required
|
9
|
+
|
10
|
+
from .schema import RegisterInputSchema, RegisterOutputSchema
|
11
|
+
|
12
|
+
|
13
|
+
class RegisterApi(MethodView):
|
14
|
+
decorators = [login_required({Role.super_admin})]
|
15
|
+
|
16
|
+
@app.input(RegisterInputSchema, arg_name='data') # type: ignore
|
17
|
+
@app.output(RegisterOutputSchema) # type: ignore
|
18
|
+
def put(self, data):
|
19
|
+
from hiddifypanel import hutils
|
20
|
+
logger.info("Register child panel with unique_id: {}", data['unique_id'])
|
21
|
+
if hutils.node.is_child():
|
22
|
+
logger.error("The panel in child, not a parent nor standalone")
|
23
|
+
abort(400, 'The panel in child, not a parent nor standalone')
|
24
|
+
|
25
|
+
unique_id = data['unique_id']
|
26
|
+
name = data['name']
|
27
|
+
mode = data['mode']
|
28
|
+
|
29
|
+
child = Child.query.filter(Child.unique_id == unique_id).first()
|
30
|
+
if not child:
|
31
|
+
logger.info("Adding new child with unique_id: {}", unique_id)
|
32
|
+
child = Child(unique_id=unique_id, name=name, mode=mode)
|
33
|
+
db.session.add(child) # type: ignore
|
34
|
+
db.session.commit() # type: ignore
|
35
|
+
child = Child.query.filter(Child.unique_id == unique_id).first()
|
36
|
+
|
37
|
+
try:
|
38
|
+
# add data
|
39
|
+
logger.info("Adding admin users...")
|
40
|
+
AdminUser.bulk_register(data['panel_data']['admin_users'], commit=False)
|
41
|
+
logger.info("Adding users...")
|
42
|
+
User.bulk_register(data['panel_data']['users'], commit=False)
|
43
|
+
logger.info("Adding domains...")
|
44
|
+
bulk_register_domains(data['panel_data']['domains'], commit=False, force_child_unique_id=child.unique_id)
|
45
|
+
logger.info("Adding hconfigs...")
|
46
|
+
bulk_register_configs(data['panel_data']['hconfigs'], commit=False, froce_child_unique_id=child.unique_id)
|
47
|
+
logger.info("Adding proxies...")
|
48
|
+
Proxy.bulk_register(data['panel_data']['proxies'], commit=False, force_child_unique_id=child.unique_id)
|
49
|
+
db.session.commit() # type: ignore
|
50
|
+
except Exception as err:
|
51
|
+
with logger.contextualize(error=err):
|
52
|
+
logger.error("Error while registering data")
|
53
|
+
abort(400, str(err))
|
54
|
+
|
55
|
+
if not hutils.node.is_parent():
|
56
|
+
logger.info("Setting panel to parent mode")
|
57
|
+
set_hconfig(ConfigEnum.panel_mode, PanelMode.parent) # type: ignore
|
58
|
+
|
59
|
+
logger.info("Returning register output")
|
60
|
+
res = RegisterOutputSchema()
|
61
|
+
res.users = [u.to_schema() for u in User.query.all()] # type: ignore
|
62
|
+
res.admin_users = [a.to_schema() for a in AdminUser.query.all()] # type: ignore
|
63
|
+
res.parent_unique_id = hconfig(ConfigEnum.unique_id)
|
64
|
+
|
65
|
+
return res
|