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
@@ -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
|
@@ -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
|