hiddifypanel 10.14.0__py3-none-any.whl → 10.15.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hiddifypanel/VERSION +1 -1
- hiddifypanel/VERSION.py +2 -2
- hiddifypanel/auth.py +15 -4
- hiddifypanel/base.py +11 -3
- hiddifypanel/cache.py +43 -25
- hiddifypanel/database.py +9 -0
- hiddifypanel/drivers/singbox_api.py +2 -14
- hiddifypanel/drivers/xray_api.py +0 -4
- hiddifypanel/hutils/__init__.py +1 -0
- hiddifypanel/hutils/convert.py +13 -2
- hiddifypanel/hutils/crypto.py +21 -2
- hiddifypanel/hutils/flask.py +18 -4
- hiddifypanel/hutils/importer/xui.py +5 -2
- hiddifypanel/hutils/node/__init__.py +3 -0
- hiddifypanel/hutils/node/api_client.py +76 -0
- hiddifypanel/hutils/node/child.py +147 -0
- hiddifypanel/hutils/node/parent.py +100 -0
- hiddifypanel/hutils/node/shared.py +65 -0
- hiddifypanel/hutils/proxy/shared.py +15 -3
- hiddifypanel/models/__init__.py +2 -2
- hiddifypanel/models/admin.py +14 -2
- hiddifypanel/models/base_account.py +3 -3
- hiddifypanel/models/child.py +30 -16
- hiddifypanel/models/config.py +39 -15
- hiddifypanel/models/config_enum.py +55 -8
- hiddifypanel/models/domain.py +28 -20
- hiddifypanel/models/parent_domain.py +2 -2
- hiddifypanel/models/proxy.py +13 -4
- hiddifypanel/models/report.py +2 -3
- hiddifypanel/models/usage.py +2 -2
- hiddifypanel/models/user.py +13 -4
- hiddifypanel/panel/admin/Actions.py +4 -6
- hiddifypanel/panel/admin/AdminstratorAdmin.py +13 -2
- hiddifypanel/panel/admin/Dashboard.py +5 -10
- hiddifypanel/panel/admin/DomainAdmin.py +12 -11
- hiddifypanel/panel/admin/NodeAdmin.py +6 -2
- hiddifypanel/panel/admin/ProxyAdmin.py +4 -3
- hiddifypanel/panel/admin/SettingAdmin.py +60 -21
- hiddifypanel/panel/admin/UserAdmin.py +10 -2
- hiddifypanel/panel/admin/templates/index.html +1 -1
- hiddifypanel/panel/admin/templates/parent_dash.html +2 -4
- hiddifypanel/panel/cli.py +16 -16
- hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +10 -5
- hiddifypanel/panel/commercial/__init__.py +7 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/__init__.py +0 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_info_api.py +2 -2
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_log_api.py +4 -5
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_user_api.py +8 -35
- hiddifypanel/panel/commercial/restapi/v2/admin/admin_users_api.py +4 -4
- hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +157 -0
- hiddifypanel/panel/commercial/restapi/v2/admin/server_status_api.py +3 -3
- hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +9 -73
- hiddifypanel/panel/commercial/restapi/v2/admin/users_api.py +1 -1
- hiddifypanel/panel/commercial/restapi/v2/child/__init__.py +18 -0
- hiddifypanel/panel/commercial/restapi/v2/child/actions.py +63 -0
- hiddifypanel/panel/commercial/restapi/v2/child/register_parent_api.py +34 -0
- hiddifypanel/panel/commercial/restapi/v2/child/schema.py +7 -0
- hiddifypanel/panel/commercial/restapi/v2/child/sync_parent_api.py +21 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/__init__.py +13 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/info.py +18 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/ping_pong.py +23 -0
- hiddifypanel/panel/commercial/restapi/v2/panel/schema.py +7 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/__init__.py +16 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/register_api.py +65 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/schema.py +115 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/status_api.py +26 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/sync_api.py +53 -0
- hiddifypanel/panel/commercial/restapi/v2/parent/usage_api.py +57 -0
- hiddifypanel/panel/commercial/telegrambot/admin.py +1 -2
- hiddifypanel/panel/common.py +21 -6
- hiddifypanel/panel/hiddify.py +9 -80
- hiddifypanel/panel/init_db.py +43 -12
- hiddifypanel/panel/usage.py +28 -15
- hiddifypanel/panel/user/templates/home/usage.html +1 -1
- hiddifypanel/panel/user/templates/new.html +1 -1
- hiddifypanel/static/css/custom.css +13 -0
- hiddifypanel/static/images/hiddify.png +0 -0
- hiddifypanel/static/new/assets/{index-bce9b1a6.js → index-ccb9873c.js} +65 -65
- hiddifypanel/templates/admin-layout.html +24 -40
- hiddifypanel/templates/fake.html +298 -0
- hiddifypanel/templates/master.html +23 -41
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +90 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +91 -1
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +98 -6
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +90 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +92 -2
- hiddifypanel/translations.i18n/en.json +56 -0
- hiddifypanel/translations.i18n/fa.json +57 -1
- hiddifypanel/translations.i18n/pt.json +63 -7
- hiddifypanel/translations.i18n/ru.json +56 -0
- hiddifypanel/translations.i18n/zh.json +58 -2
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/METADATA +47 -47
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/RECORD +104 -86
- hiddifypanel/panel/commercial/restapi/v2/DTO.py +0 -9
- hiddifypanel/panel/commercial/restapi/v2/hello/__init__.py +0 -16
- hiddifypanel/panel/commercial/restapi/v2/hello/hello.py +0 -32
- /hiddifypanel/static/images/{hiddify1.png → hiddify-old.png} +0 -0
- /hiddifypanel/static/{new/assets/hiddify-logo-noroz-559c8dcb.png → images/hiddify2.png} +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/WHEEL +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-10.14.0.dist-info → hiddifypanel-10.15.0.dev0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,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'''
|
hiddifypanel/panel/init_db.py
CHANGED
@@ -2,24 +2,50 @@ import datetime
|
|
2
2
|
import json
|
3
3
|
import os
|
4
4
|
import random
|
5
|
-
import string
|
6
5
|
import sys
|
7
6
|
import uuid
|
8
7
|
|
9
|
-
from dateutil import relativedelta
|
10
8
|
|
11
9
|
from hiddifypanel import Events, hutils
|
12
10
|
from hiddifypanel.models import *
|
13
|
-
from hiddifypanel.models import ConfigEnum, User, set_hconfig, ChildMode
|
14
11
|
from hiddifypanel.panel import hiddify
|
15
|
-
from hiddifypanel.database import db
|
12
|
+
from hiddifypanel.database import db, db_execute
|
16
13
|
from hiddifypanel import hutils
|
17
|
-
|
14
|
+
from sqlalchemy import text
|
18
15
|
from flask import g
|
19
16
|
|
20
17
|
MAX_DB_VERSION = 90
|
21
18
|
|
22
19
|
|
20
|
+
def _v83(child_id):
|
21
|
+
set_hconfig(ConfigEnum.log_level, LogLevel.CRITICAL)
|
22
|
+
|
23
|
+
|
24
|
+
def _v82(child_id):
|
25
|
+
set_hconfig(ConfigEnum.vless_enable, True)
|
26
|
+
set_hconfig(ConfigEnum.trojan_enable, True)
|
27
|
+
set_hconfig(ConfigEnum.reality_enable, True)
|
28
|
+
set_hconfig(ConfigEnum.tcp_enable, True)
|
29
|
+
set_hconfig(ConfigEnum.quic_enable, True)
|
30
|
+
set_hconfig(ConfigEnum.xtls_enable, True)
|
31
|
+
set_hconfig(ConfigEnum.h2_enable, True)
|
32
|
+
|
33
|
+
|
34
|
+
def _v80(child_id):
|
35
|
+
set_hconfig(ConfigEnum.parent_domain, '')
|
36
|
+
set_hconfig(ConfigEnum.parent_admin_proxy_path, '')
|
37
|
+
|
38
|
+
|
39
|
+
def _v79(child_id):
|
40
|
+
set_hconfig(ConfigEnum.panel_mode, PanelMode.standalone)
|
41
|
+
|
42
|
+
|
43
|
+
def _v78(child_id):
|
44
|
+
# equalize panel unique id and root child unique id
|
45
|
+
root_child_unique_id = Child.query.filter(Child.name == "Root").first().unique_id
|
46
|
+
set_hconfig(ConfigEnum.unique_id, root_child_unique_id)
|
47
|
+
|
48
|
+
|
23
49
|
def _v77(child_id):
|
24
50
|
pass
|
25
51
|
|
@@ -229,7 +255,7 @@ def _v33():
|
|
229
255
|
|
230
256
|
def _v31():
|
231
257
|
add_config_if_not_exist(ConfigEnum.reality_short_ids, uuid.uuid4().hex[0:random.randint(1, 8) * 2])
|
232
|
-
key_pair =
|
258
|
+
key_pair = hutils.crypto.generate_x25519_keys()
|
233
259
|
add_config_if_not_exist(ConfigEnum.reality_private_key, key_pair['private_key'])
|
234
260
|
add_config_if_not_exist(ConfigEnum.reality_public_key, key_pair['public_key'])
|
235
261
|
db.session.bulk_save_objects(get_proxy_rows_v1())
|
@@ -511,7 +537,7 @@ def make_proxy_rows(cfgs):
|
|
511
537
|
|
512
538
|
def add_config_if_not_exist(key: ConfigEnum, val: str | int, child_id: int | None = None):
|
513
539
|
if child_id is None:
|
514
|
-
child_id = Child.current.id
|
540
|
+
child_id = Child.current().id
|
515
541
|
|
516
542
|
old_val = hconfig(key, child_id)
|
517
543
|
print(key, val, child_id, old_val)
|
@@ -522,14 +548,16 @@ def add_config_if_not_exist(key: ConfigEnum, val: str | int, child_id: int | Non
|
|
522
548
|
def add_column(column):
|
523
549
|
try:
|
524
550
|
column_type = column.type.compile(db.engine.dialect)
|
525
|
-
|
551
|
+
|
552
|
+
db_execute(f'ALTER TABLE {column.table.name} ADD COLUMN {column.name} {column_type}')
|
526
553
|
except BaseException:
|
527
554
|
pass
|
528
555
|
|
529
556
|
|
530
|
-
def execute(query):
|
557
|
+
def execute(query: str):
|
531
558
|
try:
|
532
|
-
|
559
|
+
|
560
|
+
db_execute(query)
|
533
561
|
except BaseException as e:
|
534
562
|
print(e)
|
535
563
|
pass
|
@@ -551,7 +579,8 @@ def add_new_enum_values():
|
|
551
579
|
# Get the values in the enum column in the database
|
552
580
|
# result = db.engine.execute(f"SELECT DISTINCT `{column_name}` FROM {table_name}")
|
553
581
|
# db_values = {row[0] for row in result}
|
554
|
-
|
582
|
+
|
583
|
+
result = db_execute(f"SHOW COLUMNS FROM {table_name} LIKE '{column_name}';")
|
555
584
|
db_values = []
|
556
585
|
|
557
586
|
for row in result:
|
@@ -570,7 +599,7 @@ def add_new_enum_values():
|
|
570
599
|
# Add the new value to the enum column in the database
|
571
600
|
enumstr = ','.join([f"'{a}'" for a in [*existing_values, *old_values]])
|
572
601
|
|
573
|
-
|
602
|
+
db_execute(text(f"ALTER TABLE {table_name} MODIFY COLUMN `{column_name}` ENUM({enumstr});"))
|
574
603
|
|
575
604
|
db.session.commit()
|
576
605
|
|
@@ -674,6 +703,8 @@ def migrate(db_version):
|
|
674
703
|
for column in table_obj.columns:
|
675
704
|
add_column(column)
|
676
705
|
Events.db_prehook.notify()
|
706
|
+
if db_version < 82:
|
707
|
+
execute('ALTER TABLE child DROP INDEX `name`;')
|
677
708
|
if db_version < 77:
|
678
709
|
execute('ALTER TABLE user_detail DROP COLUMN connected_ips;')
|
679
710
|
execute('update user_detail set connected_devices="" where connected_devices IS NULL')
|
hiddifypanel/panel/usage.py
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
from flask_babel import lazy_gettext as _
|
2
2
|
from sqlalchemy import func
|
3
|
+
from typing import Dict
|
3
4
|
import datetime
|
4
5
|
|
5
6
|
from hiddifypanel.drivers import user_driver
|
6
7
|
from hiddifypanel.models import *
|
7
8
|
from hiddifypanel.panel import hiddify
|
8
9
|
from hiddifypanel.database import db
|
10
|
+
from hiddifypanel import hutils
|
9
11
|
|
10
12
|
to_gig_d = 1024**3
|
11
13
|
|
@@ -17,20 +19,18 @@ def update_local_usage():
|
|
17
19
|
# return {"status": 'success', "comments":res}
|
18
20
|
|
19
21
|
|
20
|
-
def add_users_usage_uuid(uuids_bytes, child_id):
|
22
|
+
def add_users_usage_uuid(uuids_bytes: Dict[str, Dict], child_id, sync=False):
|
21
23
|
uuids_bytes = {u: v for u, v in uuids_bytes.items() if v}
|
22
|
-
uuids = keys(
|
24
|
+
uuids = uuids_bytes.keys()
|
23
25
|
users = User.query.filter(User.uuid.in_(uuids))
|
24
26
|
dbusers_bytes = {u: uuids_bytes.get(u.uuid, 0) for u in users}
|
25
|
-
_add_users_usage(dbusers_bytes, child_id)
|
27
|
+
_add_users_usage(dbusers_bytes, child_id, sync) # type: ignore
|
26
28
|
|
27
29
|
|
28
|
-
def _add_users_usage(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
hiddify_api.add_user_usage_to_parent(dbusers_bytes)
|
33
|
-
|
30
|
+
def _add_users_usage(users_usage_data: Dict[User, Dict], child_id, sync=False):
|
31
|
+
'''
|
32
|
+
sync: when enabled, it means we have received usages from the parent panel
|
33
|
+
'''
|
34
34
|
res = {}
|
35
35
|
have_change = False
|
36
36
|
before_enabled_users = user_driver.get_enabled_users()
|
@@ -44,7 +44,7 @@ def _add_users_usage(dbusers_bytes, child_id):
|
|
44
44
|
daily_usage[adm.id].online = User.query.filter(User.added_by == adm.id).filter(func.DATE(User.last_online) == today).count()
|
45
45
|
# db.session.commit()
|
46
46
|
userDetails = {p.user_id: p for p in UserDetail.query.filter(UserDetail.child_id == child_id).all()}
|
47
|
-
for user, uinfo in
|
47
|
+
for user, uinfo in users_usage_data.items():
|
48
48
|
usage_bytes = uinfo['usage']
|
49
49
|
devices = uinfo['devices']
|
50
50
|
# user_active_before=user.is_active
|
@@ -68,11 +68,20 @@ def _add_users_usage(dbusers_bytes, child_id):
|
|
68
68
|
if not isinstance(usage_bytes, int) or usage_bytes == 0:
|
69
69
|
res[user.uuid] = "No usage"
|
70
70
|
else:
|
71
|
-
|
71
|
+
if sync:
|
72
|
+
if daily_usage.get(user.added_by, daily_usage[1]).usage != usage_bytes:
|
73
|
+
daily_usage.get(user.added_by, daily_usage[1]).usage = usage_bytes
|
74
|
+
else:
|
75
|
+
daily_usage.get(user.added_by, daily_usage[1]).usage += usage_bytes
|
72
76
|
in_gig = (usage_bytes) / to_gig_d
|
73
77
|
res[user.uuid] = in_gig
|
74
|
-
|
75
|
-
|
78
|
+
if sync:
|
79
|
+
if user.current_usage_GB != in_gig:
|
80
|
+
user.current_usage_GB = in_gig
|
81
|
+
# detail.current_usage_GB = in_gig
|
82
|
+
else:
|
83
|
+
user.current_usage_GB += in_gig
|
84
|
+
detail.current_usage_GB += in_gig
|
76
85
|
user.last_online = datetime.datetime.now()
|
77
86
|
detail.last_online = datetime.datetime.now()
|
78
87
|
|
@@ -85,15 +94,19 @@ def _add_users_usage(dbusers_bytes, child_id):
|
|
85
94
|
have_change = True
|
86
95
|
res[user.uuid] = f"{res[user.uuid]} !OUT of USAGE! Client Removed"
|
87
96
|
|
88
|
-
db.session.commit()
|
97
|
+
db.session.commit() # type: ignore
|
89
98
|
if have_change:
|
90
99
|
hiddify.quick_apply_users()
|
91
100
|
|
101
|
+
# sync with the parent
|
102
|
+
if not sync:
|
103
|
+
if hutils.node.is_child():
|
104
|
+
hutils.node.child.sync_users_usage_with_parent()
|
92
105
|
return {"status": 'success', "comments": res}
|
93
106
|
|
94
107
|
|
95
108
|
def send_bot_message(user):
|
96
|
-
if not (hconfig(ConfigEnum.telegram_bot_token)
|
109
|
+
if not (hconfig(ConfigEnum.telegram_bot_token) or not hutils.node.is_parent()):
|
97
110
|
return
|
98
111
|
if not user.telegram_id:
|
99
112
|
return
|
@@ -23,7 +23,7 @@
|
|
23
23
|
{% endif %}
|
24
24
|
{% if not user.is_active %}
|
25
25
|
{{_("User is inactive")}}
|
26
|
-
{% if (user.
|
26
|
+
{% if (user.devices|length)>user.max_ips: %}
|
27
27
|
<br>
|
28
28
|
<i class="fa-solid fa-users-slash text-danger"></i> {{_("Too many Connected IPs")}}
|
29
29
|
{% endif %}
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<link rel="manifest" href="{{ url_for('common_bp.LoginView:create_pwa_manifest',secret_uuid=g.account.uuid or '')}}">
|
14
14
|
|
15
15
|
<title>Hiddify | Panel</title>
|
16
|
-
<script type="module" crossorigin src="../static/new/assets/index-
|
16
|
+
<script type="module" crossorigin src="../static/new/assets/index-ccb9873c.js"></script>
|
17
17
|
<link rel="stylesheet" href="../static/new/assets/index-fa00de9a.css">
|
18
18
|
</head>
|
19
19
|
<body>
|
@@ -555,4 +555,17 @@ td.col-uuid a {
|
|
555
555
|
|
556
556
|
.select2-container {
|
557
557
|
display: none;
|
558
|
+
}
|
559
|
+
|
560
|
+
|
561
|
+
#main-sidebar .nav-sidebar>.nav-item>.nav-link {
|
562
|
+
color: white;
|
563
|
+
}
|
564
|
+
|
565
|
+
#main-sidebar .nav-sidebar>.nav-item>.nav-link:hover {
|
566
|
+
background: #007bff;
|
567
|
+
}
|
568
|
+
|
569
|
+
.dark-mode #main-sidebar .nav-sidebar>.nav-item>.nav-link:hover {
|
570
|
+
background: #3f6791;
|
558
571
|
}
|
Binary file
|