hiddifypanel 9.0.0.dev54__py3-none-any.whl → 9.0.0.dev60__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/base.py +1 -1
- hiddifypanel/hutils/utils.py +8 -0
- hiddifypanel/models/config_enum.py +23 -1
- hiddifypanel/panel/admin/AdminstratorAdmin.py +1 -1
- hiddifypanel/panel/admin/SettingAdmin.py +9 -1
- hiddifypanel/panel/admin/UserAdmin.py +3 -2
- hiddifypanel/panel/auth.py +1 -1
- hiddifypanel/panel/commercial/restapi/v1/tgbot.py +1 -2
- hiddifypanel/panel/commercial/restapi/v1/tgmsg.py +37 -29
- hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +15 -7
- hiddifypanel/panel/commercial/telegrambot/Usage.py +2 -1
- hiddifypanel/panel/common.py +2 -3
- hiddifypanel/panel/common_bp/login.py +3 -4
- hiddifypanel/panel/hiddify.py +2 -1
- hiddifypanel/panel/init_db.py +12 -0
- hiddifypanel/panel/user/templates/new.html +4 -2
- hiddifypanel/panel/user/user.py +59 -20
- hiddifypanel/static/new/assets/{index-bd9ba5e9.js → index-2cd90979.js} +1 -1
- hiddifypanel/templates/fake.html +2 -2
- hiddifypanel/templates/master.html +1 -1
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +131 -21
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +109 -21
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +116 -29
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +116 -29
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +109 -21
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev60.dist-info}/METADATA +1 -1
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev60.dist-info}/RECORD +38 -38
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev60.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev60.dist-info}/WHEEL +0 -0
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev60.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev60.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
9.0.0.
|
1
|
+
9.0.0.dev60
|
hiddifypanel/VERSION.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
__version__='9.0.0.
|
1
|
+
__version__='9.0.0.dev60'
|
2
2
|
from datetime import datetime
|
3
|
-
__release_date__= datetime.strptime('2024-01-
|
3
|
+
__release_date__= datetime.strptime('2024-01-21','%Y-%m-%d')
|
hiddifypanel/base.py
CHANGED
@@ -91,7 +91,7 @@ def create_app(cli=False, **config):
|
|
91
91
|
if "admin" in request.base_url:
|
92
92
|
g.locale = hconfig(ConfigEnum.admin_lang) or hconfig(ConfigEnum.lang) or 'fa'
|
93
93
|
else:
|
94
|
-
g.locale = hconfig(ConfigEnum.lang) or
|
94
|
+
g.locale = g.account.lang if isinstance(g.get('account'), User) and g.account.lang else hconfig(ConfigEnum.lang) or 'fa'
|
95
95
|
return g.locale
|
96
96
|
|
97
97
|
from flask_wtf.csrf import CSRFProtect
|
hiddifypanel/hutils/utils.py
CHANGED
@@ -20,6 +20,7 @@ class ConfigCategory(StrEnum):
|
|
20
20
|
telegram = auto()
|
21
21
|
http = auto()
|
22
22
|
tls = auto()
|
23
|
+
mux = auto()
|
23
24
|
tls_trick = auto()
|
24
25
|
ssh = auto()
|
25
26
|
ssfaketls = auto()
|
@@ -81,6 +82,17 @@ class ConfigEnum(StrEnum):
|
|
81
82
|
tls_padding_enable = auto()
|
82
83
|
tls_padding_length = auto()
|
83
84
|
|
85
|
+
# mux
|
86
|
+
mux_enable = auto()
|
87
|
+
mux_protocol = auto()
|
88
|
+
mux_max_connections = auto()
|
89
|
+
mux_min_streams = auto()
|
90
|
+
mux_max_streams = auto()
|
91
|
+
mux_padding_enable = auto()
|
92
|
+
mux_brutal_enable = auto()
|
93
|
+
mux_brutal_up_mbps = auto()
|
94
|
+
mux_brutal_down_mbps = auto()
|
95
|
+
|
84
96
|
http_ports = auto()
|
85
97
|
kcp_ports = auto()
|
86
98
|
kcp_enable = auto()
|
@@ -219,6 +231,16 @@ class ConfigEnum(StrEnum):
|
|
219
231
|
self.tls_padding_enable: {'category': ConfigCategory.tls_trick, 'apply_mode': 'apply', 'type': bool},
|
220
232
|
self.tls_padding_length: {'category': ConfigCategory.tls_trick, 'apply_mode': 'apply'},
|
221
233
|
|
234
|
+
# mux
|
235
|
+
self.mux_enable: {'category': ConfigCategory.mux, 'apply_mode': 'apply', 'type': bool},
|
236
|
+
self.mux_protocol: {'category': ConfigCategory.mux, 'apply_mode': 'apply'},
|
237
|
+
self.mux_max_connections: {'category': ConfigCategory.mux, 'apply_mode': 'apply'},
|
238
|
+
self.mux_min_streams: {'category': ConfigCategory.mux, 'apply_mode': 'apply'},
|
239
|
+
self.mux_max_streams: {'category': ConfigCategory.mux, 'apply_mode': 'apply'},
|
240
|
+
self.mux_padding_enable: {'category': ConfigCategory.mux, 'apply_mode': 'apply', 'type': bool},
|
241
|
+
self.mux_brutal_enable: {'category': ConfigCategory.mux, 'apply_mode': 'apply', 'type': bool},
|
242
|
+
self.mux_brutal_up_mbps: {'category': ConfigCategory.mux, 'apply_mode': 'apply'},
|
243
|
+
self.mux_brutal_down_mbps: {'category': ConfigCategory.mux, 'apply_mode': 'apply'},
|
222
244
|
|
223
245
|
self.http_ports: {'category': ConfigCategory.http, 'apply_mode': 'apply'}, # http
|
224
246
|
self.kcp_ports: {'category': ConfigCategory.hidden, 'apply_mode': 'apply'},
|
@@ -257,7 +279,7 @@ class ConfigEnum(StrEnum):
|
|
257
279
|
self.tuic_port: {'category': ConfigCategory.hidden, 'apply_mode': 'apply'},
|
258
280
|
|
259
281
|
self.hysteria_enable: {'category': ConfigCategory.hidden, 'type': bool, 'apply_mode': 'apply'},
|
260
|
-
self.hysteria_port: {'category': ConfigCategory.
|
282
|
+
self.hysteria_port: {'category': ConfigCategory.hidden, 'apply_mode': 'apply'},
|
261
283
|
self.hysteria_obfs_enable: {'category': ConfigCategory.hysteria, 'type': bool, 'apply_mode': 'apply'},
|
262
284
|
self.hysteria_up_mbps: {'category': ConfigCategory.hysteria, 'apply_mode': 'apply'},
|
263
285
|
self.hysteria_down_mbps: {'category': ConfigCategory.hysteria, 'apply_mode': 'apply'},
|
@@ -168,7 +168,7 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
168
168
|
|
169
169
|
# @login_required(roles={Role.super_admin, Role.admin})
|
170
170
|
def is_accessible(self):
|
171
|
-
if login_required(roles={Role.super_admin, Role.admin})(lambda: True)() != True:
|
171
|
+
if login_required(roles={Role.super_admin, Role.admin, Role.agent})(lambda: True)() != True:
|
172
172
|
return False
|
173
173
|
return True
|
174
174
|
|
@@ -211,6 +211,9 @@ def get_config_form():
|
|
211
211
|
libs = [("python", _("lib.telegram.python")), ("tgo", _("lib.telegram.go")), ("orig", _("lib.telegram.orignal")), ("erlang", _("lib.telegram.erlang"))]
|
212
212
|
field = wtf.fields.SelectField(_("config.telegram_lib.label"), choices=libs, description=_(
|
213
213
|
"config.telegram_lib.description"), default=hconfig(ConfigEnum.telegram_lib))
|
214
|
+
elif c.key == ConfigEnum.mux_protocol:
|
215
|
+
choices = [("smux", 'smux'), ("yamux", "yamux"), ("h2mux", "h2mux")]
|
216
|
+
field = wtf.fields.SelectField(_(f"config.{c.key}.label"), choices=choices, description=_(f"config.{c.key}.description"))
|
214
217
|
elif c.key == ConfigEnum.warp_sites:
|
215
218
|
validators = [wtf.validators.Length(max=2048)]
|
216
219
|
render_kw = {'class': "ltr", 'maxlength': 2048}
|
@@ -265,9 +268,14 @@ def get_config_form():
|
|
265
268
|
render_kw['required'] = ""
|
266
269
|
else:
|
267
270
|
validators.append(wtf.validators.Regexp("^(\d+)(,\d+)*$|^$", re.IGNORECASE, _("config.Invalid port")))
|
268
|
-
|
269
271
|
# validators.append(wtf.validators.Regexp("^(\d+)(,\d+)*$",re.IGNORECASE,_("config.port is required")))
|
270
272
|
|
273
|
+
# tls tricks validations
|
274
|
+
if c.key in [ConfigEnum.tls_fragment_size, ConfigEnum.tls_fragment_sleep, ConfigEnum.tls_padding_length]:
|
275
|
+
validators.append(wtf.validators.Regexp("^\d+-\d+$", re.IGNORECASE, _("config.Invalid! The pattern is number-number")+f' {c.key}'))
|
276
|
+
# mux and hysteria validations
|
277
|
+
if c.key in [ConfigEnum.hysteria_up_mbps, ConfigEnum.hysteria_down_mbps, ConfigEnum.mux_max_connections, ConfigEnum.mux_min_streams, ConfigEnum.mux_max_streams, ConfigEnum.mux_brutal_down_mbps, ConfigEnum.mux_brutal_up_mbps]:
|
278
|
+
validators.append(wtf.validators.Regexp("^\d+$", re.IGNORECASE, _("config.Invalid! it should be a number only")+f' {c.key}'))
|
271
279
|
for val in validators:
|
272
280
|
if hasattr(val, "regex"):
|
273
281
|
render_kw['pattern'] = val.regex.pattern
|
@@ -121,7 +121,7 @@ class UserAdmin(AdminLTEModelView):
|
|
121
121
|
# print("model.telegram_id",model.telegram_id)
|
122
122
|
extra = ""
|
123
123
|
if hconfig(ConfigEnum.telegram_bot_token):
|
124
|
-
if model.telegram_id:
|
124
|
+
if model.telegram_id and model.telegram_id != '0':
|
125
125
|
extra = f'<button class="btn hbtn bg-h-blue btn-xs " onclick="show_send_message({model.id})" ><i class="fa-solid fa-paper-plane"></i></button> '
|
126
126
|
else:
|
127
127
|
extra = f'<button class="btn hbtn bg-h-grey btn-xs disabled"><i class="fa-solid fa-paper-plane"></i></button> '
|
@@ -249,9 +249,10 @@ class UserAdmin(AdminLTEModelView):
|
|
249
249
|
flash(('<div id="show-modal-donation"></div>'), ' d-none')
|
250
250
|
if not re.match("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", model.uuid):
|
251
251
|
raise ValidationError('Invalid UUID e.g.,' + str(uuid.uuid4()))
|
252
|
-
|
253
252
|
if form.reset_usage.data:
|
254
253
|
model.current_usage_GB = 0
|
254
|
+
# if model.telegram_id and model.telegram_id != '0' and not re.match(r"^[1-9]\d*$", model.telegram_id):
|
255
|
+
# raise ValidationError('Invalid Telegram ID')
|
255
256
|
# if form.disable_user.data:
|
256
257
|
# model.mode=UserMode.disable
|
257
258
|
if form.reset_days.data:
|
hiddifypanel/panel/auth.py
CHANGED
@@ -155,7 +155,7 @@ def init_app(app):
|
|
155
155
|
g.is_admin = hiddify.is_admin_role(account.role) # type: ignore
|
156
156
|
login_user(account, force=True)
|
157
157
|
# print("loggining in")
|
158
|
-
if next_url is not None and g.user_agent['is_browser']:
|
158
|
+
if next_url is not None and g.user_agent['is_browser'] and ".webmanifest" not in request.path:
|
159
159
|
return redirect(next_url)
|
160
160
|
|
161
161
|
@app.url_value_preprocessor
|
@@ -56,8 +56,7 @@ def init_app(app):
|
|
56
56
|
|
57
57
|
|
58
58
|
class TGBotResource(Resource):
|
59
|
-
|
60
|
-
def post(self, admin_uuid):
|
59
|
+
def post(self, admin_uuid=None):
|
61
60
|
try:
|
62
61
|
if request.headers.get('content-type') == 'application/json':
|
63
62
|
json_string = request.get_data().decode('utf-8')
|
@@ -1,46 +1,27 @@
|
|
1
|
-
from
|
1
|
+
from typing import List
|
2
|
+
from flask import g, request
|
2
3
|
from apiflask import abort
|
3
4
|
from flask_restful import Resource
|
4
5
|
# from flask_simplelogin import login_required
|
5
6
|
import datetime
|
6
|
-
|
7
|
+
|
8
|
+
from hiddifypanel import hutils
|
7
9
|
from hiddifypanel.models import *
|
8
|
-
from hiddifypanel.panel.auth import login_required
|
9
|
-
from hiddifypanel.panel import hiddify, usage
|
10
10
|
from .tgbot import bot
|
11
11
|
|
12
12
|
|
13
13
|
class SendMsgResource(Resource):
|
14
14
|
# decorators = [login_required({Role.super_admin})]
|
15
|
-
|
16
|
-
def post(self, admin_uuid):
|
15
|
+
def post(self, admin_uuid=None):
|
17
16
|
|
18
17
|
if not hconfig(ConfigEnum.telegram_bot_token) or not bot:
|
19
18
|
abort(400, 'invalid request')
|
20
19
|
|
21
20
|
msg = request.json
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
elif id == 'all':
|
27
|
-
users = users.all()
|
28
|
-
else:
|
29
|
-
users = users.all()
|
30
|
-
if id == 'expired':
|
31
|
-
users = [u for u in users if not u.is_active]
|
32
|
-
elif id == 'active':
|
33
|
-
users = [u for u in users if u.is_active]
|
34
|
-
elif id == 'offline 1h':
|
35
|
-
h1 = datetime.datetime.now()-datetime.timedelta(hours=1)
|
36
|
-
users = [u for u in users if u.is_active and u.last_online < h1]
|
37
|
-
elif id == 'offline 1d':
|
38
|
-
d1 = datetime.datetime.now()-datetime.timedelta(hours=24)
|
39
|
-
users = [u for u in users if u.is_active and u.last_online < d1]
|
40
|
-
|
41
|
-
elif id == 'offline 1w':
|
42
|
-
d7 = datetime.datetime.now()-datetime.timedelta(days=7)
|
43
|
-
users = [u for u in users if u.is_active and u.last_online < d7]
|
21
|
+
if not msg or not msg.get('id') or not msg.get('text'):
|
22
|
+
abort(400, 'invalid request')
|
23
|
+
|
24
|
+
users = self.get_users_by_identifier(msg['id'])
|
44
25
|
|
45
26
|
res = {}
|
46
27
|
for user in users:
|
@@ -51,9 +32,36 @@ class SendMsgResource(Resource):
|
|
51
32
|
print('sending to ', user)
|
52
33
|
bot.send_message(user.telegram_id, txt, reply_markup=keyboard)
|
53
34
|
except Exception as e:
|
54
|
-
import traceback
|
55
35
|
res[user.uuid] = {'name': user.name, 'error': f'{e}'}
|
56
36
|
if len(res) == 0:
|
57
37
|
return {'msg': "success"}
|
58
38
|
else:
|
59
39
|
return {'msg': 'error', 'res': res}
|
40
|
+
|
41
|
+
def get_users_by_identifier(self, identifier: str) -> List[User]:
|
42
|
+
'''Returns all users that match the identifier for sending a message to them'''
|
43
|
+
# when we are here we must have g.account but ...
|
44
|
+
if not hasattr(g, 'account'):
|
45
|
+
return []
|
46
|
+
query = User.query.filter(User.added_by.in_(g.account.recursive_sub_admins_ids()))
|
47
|
+
query = query.filter(User.telegram_id != None, User.telegram_id != 0)
|
48
|
+
|
49
|
+
if hutils.utils.is_int(identifier):
|
50
|
+
return [query.filter(User.id == int(identifier)).first() or abort(404, 'The user not found')] # type: ignore
|
51
|
+
elif identifier == 'all':
|
52
|
+
return query.all()
|
53
|
+
elif identifier == 'expired':
|
54
|
+
return [u for u in query.all() if not u.is_active]
|
55
|
+
elif identifier == 'active':
|
56
|
+
return [u for u in query.all() if u.is_active]
|
57
|
+
elif identifier == 'offline 1h':
|
58
|
+
h1 = datetime.datetime.now()-datetime.timedelta(hours=1)
|
59
|
+
return [u for u in query.all() if u.is_active and u.last_online < h1]
|
60
|
+
elif identifier == 'offline 1d':
|
61
|
+
d1 = datetime.datetime.now()-datetime.timedelta(hours=24)
|
62
|
+
return [u for u in query.all() if u.is_active and u.last_online < d1]
|
63
|
+
elif identifier == 'offline 1w':
|
64
|
+
d7 = datetime.datetime.now()-datetime.timedelta(days=7)
|
65
|
+
return [u for u in query.all() if u.is_active and u.last_online < d7]
|
66
|
+
else:
|
67
|
+
return []
|
@@ -46,14 +46,17 @@ class AllConfigsAPI(MethodView):
|
|
46
46
|
items.append(
|
47
47
|
create_item(
|
48
48
|
"Auto", "ALL", "ALL", "", "", "",
|
49
|
-
f"{base_url}sub/?asn={c['asn']}"
|
49
|
+
# f"{base_url}sub/?asn={c['asn']}"
|
50
|
+
f"{base_url}auto/?asn={c['asn']}"
|
51
|
+
)
|
50
52
|
)
|
51
53
|
|
52
54
|
# Add Full Singbox
|
53
55
|
items.append(
|
54
56
|
create_item(
|
55
57
|
"Full Singbox", "ALL", "ALL", "", "", "",
|
56
|
-
f"{base_url}full-singbox.json?asn={c['asn']}"
|
58
|
+
# f"{base_url}full-singbox.json?asn={c['asn']}"
|
59
|
+
f"{base_url}singbox/?asn={c['asn']}"
|
57
60
|
)
|
58
61
|
)
|
59
62
|
|
@@ -61,7 +64,8 @@ class AllConfigsAPI(MethodView):
|
|
61
64
|
items.append(
|
62
65
|
create_item(
|
63
66
|
"Clash Meta", "ALL", "ALL", "", "", "",
|
64
|
-
f"clashmeta://install-config?url={base_url}clash/meta/all.yml&name=mnormal_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}"
|
67
|
+
# f"clashmeta://install-config?url={base_url}clash/meta/all.yml&name=mnormal_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}"
|
68
|
+
f"clash://install-config?url={base_url}clashmeta/?asn={c['asn']}"
|
65
69
|
)
|
66
70
|
)
|
67
71
|
|
@@ -69,7 +73,8 @@ class AllConfigsAPI(MethodView):
|
|
69
73
|
items.append(
|
70
74
|
create_item(
|
71
75
|
"Clash", "ALL", "Except VLess", "", "", "",
|
72
|
-
f"clash://install-config?url={base_url}clash/all.yml&name=new_normal_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}"
|
76
|
+
# f"clash://install-config?url={base_url}clash/all.yml&name=new_normal_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}"
|
77
|
+
f"clash://install-config?url={base_url}clash/?asn={c['asn']}"
|
73
78
|
)
|
74
79
|
)
|
75
80
|
|
@@ -78,7 +83,8 @@ class AllConfigsAPI(MethodView):
|
|
78
83
|
items.append(
|
79
84
|
create_item(
|
80
85
|
"Singbox: SSH", "SSH", "SHH", "", "", "",
|
81
|
-
f"{base_url}singbox.json?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
|
86
|
+
# f"{base_url}singbox.json?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
|
87
|
+
f"{base_url}singbox-ssh/?asn={c['asn']}"
|
82
88
|
)
|
83
89
|
)
|
84
90
|
|
@@ -86,7 +92,8 @@ class AllConfigsAPI(MethodView):
|
|
86
92
|
items.append(
|
87
93
|
create_item(
|
88
94
|
"Subscription link", "ALL", "ALL", "", "", "",
|
89
|
-
f"{base_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
|
95
|
+
# f"{base_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
|
96
|
+
f"{base_url}sub/?asn={c['asn']}"
|
90
97
|
)
|
91
98
|
)
|
92
99
|
|
@@ -94,7 +101,8 @@ class AllConfigsAPI(MethodView):
|
|
94
101
|
items.append(
|
95
102
|
create_item(
|
96
103
|
"Subscription link b64", "ALL", "ALL", "", "", "",
|
97
|
-
f"{base_url}all.txt?name=new_link_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}&base64=True"
|
104
|
+
# f"{base_url}all.txt?name=new_link_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}&base64=True"
|
105
|
+
f"{base_url}sub64/?asn={c['asn']}"
|
98
106
|
)
|
99
107
|
)
|
100
108
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from hiddifypanel.panel import hiddify
|
1
2
|
from telebot import types
|
2
3
|
from flask_babelex import gettext as _
|
3
4
|
from flask import current_app as app
|
@@ -52,7 +53,7 @@ def get_usage_msg(uuid, domain=None):
|
|
52
53
|
reset_day = user_data['reset_day']
|
53
54
|
|
54
55
|
domain = domain or get_panel_domains()[0]
|
55
|
-
user_link = hiddify.get_account_panel_link(user,domain.domain)
|
56
|
+
user_link = hiddify.get_account_panel_link(user, domain.domain)
|
56
57
|
msg = f"""{_('<a href="%(user_link)s"> %(user)s</a>',user_link=user_link ,user=user.name if user.name != "default" else "")}\n\n"""
|
57
58
|
|
58
59
|
msg += f"""{_('user.home.usage.title')} {round(user.current_usage_GB, 3)}GB <b>{_('user.home.usage.from')}</b> {user.usage_limit_GB}GB {_('user.home.usage.monthly') if user.monthly else ''}\n"""
|
hiddifypanel/panel/common.py
CHANGED
@@ -100,7 +100,6 @@ def init_app(app: APIFlask):
|
|
100
100
|
@app.url_defaults
|
101
101
|
def add_proxy_path_user(endpoint, values):
|
102
102
|
if 'proxy_path' not in values:
|
103
|
-
|
104
103
|
if hasattr(g, 'account') and isinstance(g.account, AdminUser):
|
105
104
|
values['proxy_path'] = hconfig(ConfigEnum.proxy_path_admin)
|
106
105
|
# elif 'static' in endpoint:
|
@@ -114,8 +113,8 @@ def init_app(app: APIFlask):
|
|
114
113
|
|
115
114
|
if hiddify.is_api_v1_call(endpoint=endpoint) and 'admin_uuid' not in values:
|
116
115
|
values['admin_uuid'] = AdminUser.get_super_admin_uuid()
|
117
|
-
# if 'secret_uuid' not in values:
|
118
|
-
# values['secret_uuid'] =
|
116
|
+
# if 'secret_uuid' not in values and g.account and ".webmanifest" in request.path:
|
117
|
+
# values['secret_uuid'] = g.account.uuid
|
119
118
|
|
120
119
|
@app.route("/<proxy_path>/videos/<file>")
|
121
120
|
@app.doc(hide=True)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from flask_classful import FlaskView, route
|
2
2
|
from hiddifypanel.panel.auth import login_required, current_account, login_user, logout_user, login_by_uuid
|
3
|
-
from flask import redirect, request, g, url_for, render_template, flash
|
3
|
+
from flask import redirect, request, g, url_for, render_template, flash, jsonify
|
4
4
|
from flask import current_app as app
|
5
5
|
from flask_babelex import lazy_gettext as _
|
6
6
|
from apiflask import abort
|
@@ -118,8 +118,7 @@ class LoginView(FlaskView):
|
|
118
118
|
|
119
119
|
# return redirect(f"/{proxy_path}/{path}")
|
120
120
|
|
121
|
-
@route('
|
122
|
-
@login_required()
|
121
|
+
@route('/<secret_uuid>/manifest.webmanifest')
|
123
122
|
def create_pwa_manifest(self):
|
124
123
|
domain = request.host
|
125
124
|
name = (domain if hiddify.is_admin_panel_call() else g.account.name)
|
@@ -130,7 +129,7 @@ class LoginView(FlaskView):
|
|
130
129
|
"background_color": "#1a1b21",
|
131
130
|
"display": "standalone",
|
132
131
|
"scope": f"/",
|
133
|
-
"start_url": hiddify.get_account_panel_link(g.account, domain) +"?pwa=true",
|
132
|
+
"start_url": hiddify.get_account_panel_link(g.account, domain) + "?pwa=true",
|
134
133
|
"description": "Hiddify, for a free Internet",
|
135
134
|
"orientation": "any",
|
136
135
|
"icons": [
|
hiddifypanel/panel/hiddify.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import glob
|
2
|
+
import uuid
|
2
3
|
import user_agents
|
3
4
|
import json
|
4
5
|
import subprocess
|
@@ -876,7 +877,7 @@ def get_direct_host_or_ip(prefer_version: int):
|
|
876
877
|
else:
|
877
878
|
direct = hutils.ip.get_ip(prefer_version)
|
878
879
|
if not direct:
|
879
|
-
direct = hutils.ip.get_ip(
|
880
|
+
direct = hutils.ip.get_ip(4 if prefer_version == 6 else 6)
|
880
881
|
return direct
|
881
882
|
|
882
883
|
|
hiddifypanel/panel/init_db.py
CHANGED
@@ -148,6 +148,18 @@ def init_db():
|
|
148
148
|
# add_config_if_not_exist(ConfigEnum.hysteria_enable, True)
|
149
149
|
# add_config_if_not_exist(ConfigEnum.hysteria_port, random.randint(5000, 20000))
|
150
150
|
|
151
|
+
def _v65():
|
152
|
+
add_config_if_not_exist(ConfigEnum.mux_enable, False)
|
153
|
+
add_config_if_not_exist(ConfigEnum.mux_protocol, 'smux')
|
154
|
+
add_config_if_not_exist(ConfigEnum.mux_max_connections, '4')
|
155
|
+
add_config_if_not_exist(ConfigEnum.mux_min_streams, '4')
|
156
|
+
add_config_if_not_exist(ConfigEnum.mux_max_streams, '0')
|
157
|
+
add_config_if_not_exist(ConfigEnum.mux_padding_enable, False)
|
158
|
+
add_config_if_not_exist(ConfigEnum.mux_brutal_enable, True)
|
159
|
+
add_config_if_not_exist(ConfigEnum.mux_brutal_up_mbps, '100')
|
160
|
+
add_config_if_not_exist(ConfigEnum.mux_brutal_down_mbps, '100')
|
161
|
+
|
162
|
+
|
151
163
|
def _v64():
|
152
164
|
set_hconfig(ConfigEnum.ssh_server_redis_url, "unix:///opt/hiddify-manager/other/redis/run.sock?db=1")
|
153
165
|
|
@@ -9,9 +9,11 @@
|
|
9
9
|
<link rel="stylesheet" href="node_modules/smartbanner.js/dist/smartbanner.min.css">
|
10
10
|
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet" type="text/css" />
|
11
11
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
12
|
-
|
12
|
+
<link rel="icon" type="image/x-icon" href="{{ static_url_for( filename='images/favicon.ico')}}">
|
13
|
+
<link rel="manifest" href="{{ url_for('common_bp.LoginView:create_pwa_manifest',secret_uuid=g.account.uuid or '')}}">
|
14
|
+
|
13
15
|
<title>Hiddify | Panel</title>
|
14
|
-
<script type="module" crossorigin src="../static/new/assets/index-
|
16
|
+
<script type="module" crossorigin src="../static/new/assets/index-2cd90979.js"></script>
|
15
17
|
<link rel="stylesheet" href="../static/new/assets/index-d9bbf489.css">
|
16
18
|
</head>
|
17
19
|
<body>
|
hiddifypanel/panel/user/user.py
CHANGED
@@ -24,7 +24,7 @@ class UserView(FlaskView):
|
|
24
24
|
ua = request.user_agent.string
|
25
25
|
print(ua)
|
26
26
|
print(hiddify.get_user_agent())
|
27
|
-
return ua
|
27
|
+
return ua
|
28
28
|
|
29
29
|
def index(self):
|
30
30
|
return self.auto_sub()
|
@@ -34,12 +34,65 @@ class UserView(FlaskView):
|
|
34
34
|
return self.new()
|
35
35
|
return self.get_proper_config() or self.all_configs(base64=True)
|
36
36
|
|
37
|
-
|
38
|
-
@
|
37
|
+
# former /sub/ or /sub (it was auto actually but we named it as /sub/)
|
38
|
+
# TODO: @hiddify: check this out
|
39
|
+
@route('/auto/')
|
40
|
+
@route('/auto')
|
39
41
|
@login_required(roles={Role.user})
|
40
42
|
def force_sub(self):
|
41
43
|
return self.get_proper_config() or self.all_configs(base64=False)
|
42
44
|
|
45
|
+
# region new endpoints
|
46
|
+
@route("/sub/")
|
47
|
+
@route("/sub")
|
48
|
+
@login_required(roles={Role.user})
|
49
|
+
def sub(self):
|
50
|
+
return self.all_configs(base64=False)
|
51
|
+
|
52
|
+
@route("/sub64/")
|
53
|
+
@route("/sub64")
|
54
|
+
@login_required(roles={Role.user})
|
55
|
+
def sub64(self):
|
56
|
+
return self.all_configs(base64=True)
|
57
|
+
|
58
|
+
@route("/singbox/")
|
59
|
+
@route("/singbox")
|
60
|
+
@login_required(roles={Role.user})
|
61
|
+
def singbox_full(self):
|
62
|
+
return self.full_singbox()
|
63
|
+
|
64
|
+
@route("/singbox-ssh/")
|
65
|
+
@route("/singbox-ssh")
|
66
|
+
@login_required(roles={Role.user})
|
67
|
+
def singbox_ssh(self):
|
68
|
+
return self.singbox()
|
69
|
+
|
70
|
+
@route("/clash/")
|
71
|
+
@route("/clash")
|
72
|
+
@login_required(roles={Role.user})
|
73
|
+
def clash(self):
|
74
|
+
return self.clash_config(meta_or_normal="normal")
|
75
|
+
|
76
|
+
@route("/clashmeta/")
|
77
|
+
@route("/clashmeta")
|
78
|
+
@login_required(roles={Role.user})
|
79
|
+
def clashmeta(self):
|
80
|
+
return self.clash_config(meta_or_normal="meta")
|
81
|
+
# endregion
|
82
|
+
|
83
|
+
@ route('/new/')
|
84
|
+
@ route('/new')
|
85
|
+
@login_required(roles={Role.user})
|
86
|
+
def new(self):
|
87
|
+
conf = self.get_proper_config()
|
88
|
+
if conf:
|
89
|
+
return conf
|
90
|
+
|
91
|
+
c = get_common_data(g.account.uuid, mode="new")
|
92
|
+
user_agent = user_agents.parse(request.user_agent.string)
|
93
|
+
# return render_template('home/multi.html', **c, ua=user_agent)
|
94
|
+
return render_template('new.html', **c, ua=user_agent)
|
95
|
+
|
43
96
|
def get_proper_config(self):
|
44
97
|
if g.user_agent['is_browser']:
|
45
98
|
return None
|
@@ -59,20 +112,6 @@ class UserView(FlaskView):
|
|
59
112
|
if re.match('^(Hiddify|FoXray|Fair|v2rayNG|SagerNet|Shadowrocket|V2Box|Loon|Liberty)', ua, re.IGNORECASE):
|
60
113
|
return self.all_configs(base64=True)
|
61
114
|
|
62
|
-
@ route('/new/')
|
63
|
-
@ route('/new')
|
64
|
-
@login_required(roles={Role.user})
|
65
|
-
# @ route('/')
|
66
|
-
def new(self):
|
67
|
-
conf = self.get_proper_config()
|
68
|
-
if conf:
|
69
|
-
return conf
|
70
|
-
|
71
|
-
c = get_common_data(g.account.uuid, mode="new")
|
72
|
-
user_agent = user_agents.parse(request.user_agent.string)
|
73
|
-
# return render_template('home/multi.html', **c, ua=user_agent)
|
74
|
-
return render_template('new.html', **c, ua=user_agent)
|
75
|
-
|
76
115
|
@ route('/clash/<meta_or_normal>/proxies.yml')
|
77
116
|
@ route('/clash/proxies.yml')
|
78
117
|
@login_required(roles={Role.user})
|
@@ -185,8 +224,8 @@ class UserView(FlaskView):
|
|
185
224
|
resp = do_base_64(resp)
|
186
225
|
return add_headers(resp, c)
|
187
226
|
|
188
|
-
@login_required(roles={Role.user})
|
189
227
|
@ route("/offline.html")
|
228
|
+
@login_required(roles={Role.user})
|
190
229
|
def offline():
|
191
230
|
return f"Not Connected <a href='{hiddify.get_account_panel_link(g.account, request.host)}'>click for reload</a>"
|
192
231
|
|
@@ -307,7 +346,7 @@ def get_common_data(user_uuid, mode, no_domain=False, filter_domain=None):
|
|
307
346
|
'ConfigEnum': ConfigEnum,
|
308
347
|
'link_maker': link_maker,
|
309
348
|
'domains': domains,
|
310
|
-
"bot": g.bot,
|
349
|
+
"bot": g.get('bot', None),
|
311
350
|
"db_domain": db_domain,
|
312
351
|
"telegram_enable": hconfig(ConfigEnum.telegram_enable) and any([d for d in domains if d.mode in [DomainType.direct, DomainType.relay, DomainType.old_xtls_direct]]),
|
313
352
|
"ip": user_ip,
|
@@ -315,7 +354,7 @@ def get_common_data(user_uuid, mode, no_domain=False, filter_domain=None):
|
|
315
354
|
"asn": asn,
|
316
355
|
"country": auto_ip_selector.get_country(user_ip),
|
317
356
|
'has_auto_cdn': has_auto_cdn,
|
318
|
-
'profile_url': hiddify.get_account_panel_link(g.account, domain)
|
357
|
+
'profile_url': hiddify.get_account_panel_link(g.account if g.get('account') else user, domain)
|
319
358
|
}
|
320
359
|
|
321
360
|
|