hiddifypanel 10.80.0.dev13__py3-none-any.whl → 10.80.0.dev15__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 +1 -1
- hiddifypanel/hutils/proxy/__init__.py +1 -0
- hiddifypanel/hutils/proxy/clash.py +2 -2
- hiddifypanel/hutils/proxy/shared.py +6 -6
- hiddifypanel/hutils/proxy/singbox.py +1 -1
- hiddifypanel/hutils/proxy/wireguard.py +34 -0
- hiddifypanel/hutils/proxy/xray.py +2 -2
- hiddifypanel/hutils/proxy/xrayjson.py +4 -4
- hiddifypanel/models/config_enum.py +2 -2
- hiddifypanel/models/proxy.py +1 -1
- hiddifypanel/panel/admin/AdminstratorAdmin.py +7 -8
- hiddifypanel/panel/admin/DomainAdmin.py +131 -98
- hiddifypanel/panel/admin/QuickSetup.py +8 -8
- hiddifypanel/panel/admin/UserAdmin.py +58 -33
- hiddifypanel/panel/admin/templates/index.html +6 -4
- hiddifypanel/panel/admin/templates/model/user_list.html +11 -3
- hiddifypanel/panel/commercial/restapi/v1/tgbot.py +19 -1
- hiddifypanel/panel/common.py +1 -1
- hiddifypanel/panel/init_db.py +19 -16
- hiddifypanel/panel/user/user.py +6 -18
- hiddifypanel/translations.i18n/en.json +342 -1
- hiddifypanel/translations.i18n/fa.json +342 -1
- hiddifypanel/translations.i18n/fr.json +2 -2
- hiddifypanel/translations.i18n/my.json +2 -2
- hiddifypanel/translations.i18n/pt.json +342 -1
- hiddifypanel/translations.i18n/ru.json +342 -1
- hiddifypanel/translations.i18n/zh.json +342 -1
- {hiddifypanel-10.80.0.dev13.dist-info → hiddifypanel-10.80.0.dev15.dist-info}/METADATA +1 -1
- {hiddifypanel-10.80.0.dev13.dist-info → hiddifypanel-10.80.0.dev15.dist-info}/RECORD +33 -32
- {hiddifypanel-10.80.0.dev13.dist-info → hiddifypanel-10.80.0.dev15.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.80.0.dev13.dist-info → hiddifypanel-10.80.0.dev15.dist-info}/WHEEL +0 -0
- {hiddifypanel-10.80.0.dev13.dist-info → hiddifypanel-10.80.0.dev15.dist-info}/entry_points.txt +0 -0
@@ -27,7 +27,7 @@ from hiddifypanel import hutils
|
|
27
27
|
class UserAdmin(AdminLTEModelView):
|
28
28
|
column_default_sort = ('id', False) # Sort by username in ascending order
|
29
29
|
|
30
|
-
column_sortable_list = ["is_active", "name", "current_usage", 'mode', "remaining_days","max_ips", "comment", 'last_online', "uuid"
|
30
|
+
column_sortable_list = ["is_active", "name", "current_usage", 'mode', "remaining_days", "max_ips", "comment", 'last_online', "uuid"]
|
31
31
|
column_searchable_list = ["uuid", "name"]
|
32
32
|
column_list = ["is_active", "name", "UserLinks", "current_usage", "remaining_days", "comment", 'last_online', 'mode', 'admin', "uuid"]
|
33
33
|
column_editable_list = ["comment", "name", "uuid"]
|
@@ -131,7 +131,7 @@ class UserAdmin(AdminLTEModelView):
|
|
131
131
|
if model.is_active:
|
132
132
|
link = '<i class="fa-solid fa-circle-check text-success"></i> '
|
133
133
|
elif len(model.devices):
|
134
|
-
link = '<i class="fa-solid fa-users-slash text-danger" title="{_("Too many Connected IPs")}"></i>'
|
134
|
+
link = f'<i class="fa-solid fa-users-slash text-danger" title="{_("Too many Connected IPs")}"></i>'
|
135
135
|
else:
|
136
136
|
link = '<i class="fa-solid fa-circle-xmark text-danger"></i> '
|
137
137
|
|
@@ -161,8 +161,8 @@ class UserAdmin(AdminLTEModelView):
|
|
161
161
|
|
162
162
|
def _usage_formatter(view, context, model, name):
|
163
163
|
u = round(model.current_usage_GB, 3)
|
164
|
-
t = round(model.usage_limit_GB, 3)
|
165
|
-
rate = round(u * 100 /
|
164
|
+
t = max(round(model.usage_limit_GB, 3), 0.001) # Prevent division by zero
|
165
|
+
rate = min(round(u * 100 / t), 100) # Cap at 100%
|
166
166
|
state = "danger" if u >= t else ('warning' if rate > 80 else 'success')
|
167
167
|
color = "#ff7e7e" if u >= t else ('#ffc107' if rate > 80 else '#9ee150')
|
168
168
|
return Markup(f"""
|
@@ -225,38 +225,45 @@ class UserAdmin(AdminLTEModelView):
|
|
225
225
|
|
226
226
|
def on_form_prefill(self, form, id=None):
|
227
227
|
# print("================",form._obj.start_date)
|
228
|
-
if
|
228
|
+
if form._obj is None:
|
229
|
+
return
|
230
|
+
|
231
|
+
if id is None or form._obj.start_date is None or form._obj.current_usage==0:
|
229
232
|
msg = _("Package not started yet.")
|
230
233
|
# form.reset['class']="d-none"
|
231
|
-
|
234
|
+
if form._obj.start_date is None:
|
235
|
+
if hasattr(form, 'reset_days'):
|
232
236
|
delattr(form, 'reset_days')
|
233
|
-
if form._obj.current_usage==0:
|
234
|
-
delattr(form, 'reset_usage')
|
235
|
-
# delattr(form,'disable_user')
|
236
237
|
else:
|
237
|
-
remaining = form._obj.remaining_days
|
238
|
+
remaining = form._obj.remaining_days
|
238
239
|
relative_remaining = hutils.convert.format_timedelta(datetime.timedelta(days=remaining))
|
239
240
|
msg = _("Remaining about %(relative)s, exactly %(days)s days", relative=relative_remaining, days=remaining)
|
240
241
|
form.reset_days.label.text += f" ({msg})"
|
241
|
-
usr_usage = f" ({_('user.home.usage.title')} {round(form._obj.current_usage_GB,3)}GB)"
|
242
|
-
form.reset_usage.label.text += usr_usage
|
243
|
-
form.reset_usage.data = False
|
244
242
|
form.reset_days.data = False
|
245
243
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
form.
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
244
|
+
# Handle reset_usage field
|
245
|
+
if form._obj.current_usage == 0:
|
246
|
+
if hasattr(form, 'reset_usage'):
|
247
|
+
delattr(form, 'reset_usage')
|
248
|
+
else:
|
249
|
+
usr_usage = f" ({_('user.home.usage.title')} {round(form._obj.current_usage_GB,3)}GB)"
|
250
|
+
if hasattr(form, 'reset_usage'):
|
251
|
+
form.reset_usage.label.text += usr_usage
|
252
|
+
form.reset_usage.data = False
|
253
|
+
|
254
|
+
if hasattr(form, 'usage_limit'):
|
255
|
+
form.usage_limit.label.text += usr_usage
|
256
|
+
|
257
|
+
# Handle package days info
|
258
|
+
if form._obj.start_date and hasattr(form, 'package_days'):
|
259
|
+
started = form._obj.start_date - datetime.date.today()
|
260
|
+
msg = _("Started from %(relative)s", relative=hutils.convert.format_timedelta(started))
|
261
|
+
form.package_days.label.text += f" ({msg})"
|
262
|
+
if started.days <= 0:
|
263
|
+
exact_start = _("Started %(days)s days ago", days=-started.days)
|
264
|
+
else:
|
265
|
+
exact_start = _("Will Start in %(days)s days", days=started.days)
|
266
|
+
form.package_days.description += f" ({exact_start})"
|
260
267
|
|
261
268
|
def get_edit_form(self):
|
262
269
|
form = super().get_edit_form()
|
@@ -267,26 +274,44 @@ class UserAdmin(AdminLTEModelView):
|
|
267
274
|
return form
|
268
275
|
|
269
276
|
def on_model_change(self, form, model, is_created):
|
270
|
-
|
277
|
+
# Validate max_ips
|
278
|
+
try:
|
279
|
+
model.max_ips = max(3, min(int(model.max_ips or 10000), 10000))
|
280
|
+
except (ValueError, TypeError):
|
281
|
+
model.max_ips = 1000
|
282
|
+
|
283
|
+
# Show donation message
|
271
284
|
if len(User.query.all()) % 4 == 0:
|
272
285
|
hutils.flask.flash(('<div id="show-modal-donation"></div>'), ' d-none')
|
286
|
+
|
287
|
+
# Validate UUID
|
273
288
|
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):
|
274
289
|
raise ValidationError('Invalid UUID e.g.,' + str(uuid.uuid4()))
|
290
|
+
|
291
|
+
# Handle reset flags
|
275
292
|
if hasattr(form, 'reset_usage') and form.reset_usage.data:
|
276
293
|
model.current_usage_GB = 0
|
277
|
-
|
278
|
-
# raise ValidationError('Invalid Telegram ID')
|
279
|
-
# if form.disable_user.data:
|
280
|
-
# model.mode=UserMode.disable
|
294
|
+
|
281
295
|
if hasattr(form, 'reset_days') and form.reset_days.data:
|
282
296
|
model.start_date = None
|
283
|
-
|
297
|
+
|
298
|
+
# Validate package days
|
299
|
+
try:
|
300
|
+
model.package_days = min(int(model.package_days), 10000)
|
301
|
+
except (ValueError, TypeError):
|
302
|
+
model.package_days = 10000
|
303
|
+
|
304
|
+
# Handle user ownership
|
284
305
|
old_user = User.by_id(model.id)
|
285
306
|
if not model.added_by or model.added_by == 1:
|
286
307
|
model.added_by = g.account.id
|
308
|
+
|
309
|
+
# Validate user limits
|
287
310
|
if not g.account.can_have_more_users():
|
288
311
|
raise ValidationError(_('You have too much users! You can have only %(active)s active users and %(total)s users',
|
289
312
|
active=g.account.max_active_users, total=g.account.max_users))
|
313
|
+
|
314
|
+
# Handle UUID changes
|
290
315
|
if old_user and old_user.uuid != model.uuid:
|
291
316
|
user_driver.remove_client(old_user)
|
292
317
|
|
@@ -192,10 +192,12 @@
|
|
192
192
|
}
|
193
193
|
|
194
194
|
function update_from_json(data) {
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
195
|
+
// Use local variables instead of globals
|
196
|
+
const usage_history = data['usage_history'];
|
197
|
+
const onlines = usage_history['m5']['online'];
|
198
|
+
const total_users = usage_history['total']['users'];
|
199
|
+
const stats = data['stats'];
|
200
|
+
|
199
201
|
info_box("today", "fa-solid fa-calendar", "Today Usage",
|
200
202
|
((usage_history['today']['usage'] / Math.pow(1024, 3)).toFixed(1)) + " GB",
|
201
203
|
usage_history['today']['online'] / Math.max(1, total_users) * 100,
|
@@ -121,9 +121,17 @@
|
|
121
121
|
$(document).Toasts('create', {
|
122
122
|
class: 'bg-danger',
|
123
123
|
position: "{{'topRight' if get_locale()=='fa' else 'topLeft'}}",
|
124
|
-
title: status + " " + error,
|
125
|
-
body: JSON.stringify(jqx)
|
126
|
-
|
124
|
+
title: $('<div>').text(status + " " + error).html(),
|
125
|
+
body: $('<div>').text(JSON.stringify(jqx)).html(),
|
126
|
+
autohide: true,
|
127
|
+
delay: 5000
|
128
|
+
});
|
129
|
+
|
130
|
+
console.error('API Error:', {
|
131
|
+
status: status,
|
132
|
+
error: error,
|
133
|
+
details: jqx
|
134
|
+
});
|
127
135
|
},
|
128
136
|
success: function (data) {
|
129
137
|
// dialog.modal('hide');
|
@@ -2,6 +2,7 @@ import telebot
|
|
2
2
|
from flask import request
|
3
3
|
from apiflask import abort
|
4
4
|
from flask_restful import Resource
|
5
|
+
import time
|
5
6
|
|
6
7
|
from hiddifypanel.models import *
|
7
8
|
from hiddifypanel import Events
|
@@ -11,7 +12,24 @@ logger = telebot.logger
|
|
11
12
|
|
12
13
|
class ExceptionHandler(telebot.ExceptionHandler):
|
13
14
|
def handle(self, exception):
|
14
|
-
|
15
|
+
"""Improved error handling for Telegram bot exceptions"""
|
16
|
+
error_msg = str(exception)
|
17
|
+
logger.error(f"Telegram bot error: {error_msg}")
|
18
|
+
|
19
|
+
try:
|
20
|
+
# Attempt recovery based on error type
|
21
|
+
if "webhook" in error_msg.lower():
|
22
|
+
if hasattr(bot, 'remove_webhook'):
|
23
|
+
bot.remove_webhook()
|
24
|
+
logger.info("Removed webhook due to error")
|
25
|
+
elif "connection" in error_msg.lower():
|
26
|
+
# Wait and retry for connection issues
|
27
|
+
time.sleep(5)
|
28
|
+
return True # Indicates retry
|
29
|
+
except Exception as e:
|
30
|
+
logger.error(f"Error during recovery attempt: {str(e)}")
|
31
|
+
|
32
|
+
return False # Don't retry for unknown errors
|
15
33
|
|
16
34
|
|
17
35
|
bot = telebot.TeleBot("1:2", parse_mode="HTML", threaded=False, exception_handler=ExceptionHandler())
|
hiddifypanel/panel/common.py
CHANGED
@@ -65,7 +65,7 @@ def init_app(app: APIFlask):
|
|
65
65
|
'version': hiddifypanel.__version__,
|
66
66
|
}), 500
|
67
67
|
|
68
|
-
trace = traceback.format_exc(
|
68
|
+
trace = traceback.format_exc()
|
69
69
|
|
70
70
|
# Create github issue link
|
71
71
|
issue_link = hutils.github_issue.generate_github_issue_link_for_500_error(e, trace)
|
hiddifypanel/panel/init_db.py
CHANGED
@@ -77,7 +77,7 @@ def _v94(child_id):
|
|
77
77
|
|
78
78
|
def _v93(child_id):
|
79
79
|
set_hconfig(ConfigEnum.quic_enable, True)
|
80
|
-
set_hconfig(ConfigEnum.
|
80
|
+
set_hconfig(ConfigEnum.xhttp_enable, True)
|
81
81
|
|
82
82
|
|
83
83
|
def _v92(child_id):
|
@@ -85,9 +85,9 @@ def _v92(child_id):
|
|
85
85
|
|
86
86
|
|
87
87
|
def _v89(child_id):
|
88
|
-
set_hconfig(ConfigEnum.
|
88
|
+
set_hconfig(ConfigEnum.path_xhttp,
|
89
89
|
hutils.random.get_random_string(7, 15))
|
90
|
-
set_hconfig(ConfigEnum.
|
90
|
+
set_hconfig(ConfigEnum.xhttp_enable, False)
|
91
91
|
pass
|
92
92
|
|
93
93
|
|
@@ -218,7 +218,7 @@ def _v65():
|
|
218
218
|
add_config_if_not_exist(ConfigEnum.mux_min_streams, '4')
|
219
219
|
add_config_if_not_exist(ConfigEnum.mux_max_streams, '0')
|
220
220
|
add_config_if_not_exist(ConfigEnum.mux_padding_enable, False)
|
221
|
-
add_config_if_not_exist(ConfigEnum.mux_brutal_enable,
|
221
|
+
add_config_if_not_exist(ConfigEnum.mux_brutal_enable, False)
|
222
222
|
add_config_if_not_exist(ConfigEnum.mux_brutal_up_mbps, '100')
|
223
223
|
add_config_if_not_exist(ConfigEnum.mux_brutal_down_mbps, '100')
|
224
224
|
|
@@ -571,9 +571,9 @@ def get_proxy_rows_v1():
|
|
571
571
|
"httpupgrade direct vless",
|
572
572
|
# "httpupgrade direct trojan",
|
573
573
|
"httpupgrade direct vmess",
|
574
|
-
"
|
575
|
-
"
|
576
|
-
"
|
574
|
+
"xhttp direct vless",
|
575
|
+
"xhttp direct trojan",
|
576
|
+
"xhttp direct vmess",
|
577
577
|
"tcp direct vless",
|
578
578
|
"tcp direct trojan",
|
579
579
|
"tcp direct vmess",
|
@@ -591,9 +591,9 @@ def get_proxy_rows_v1():
|
|
591
591
|
# "httpupgrade relay trojan",
|
592
592
|
"httpupgrade relay vmess",
|
593
593
|
|
594
|
-
"
|
595
|
-
"
|
596
|
-
"
|
594
|
+
"xhttp relay vless",
|
595
|
+
"xhttp relay trojan",
|
596
|
+
"xhttp relay vmess",
|
597
597
|
|
598
598
|
"tcp relay vless",
|
599
599
|
"tcp relay trojan",
|
@@ -615,9 +615,9 @@ def get_proxy_rows_v1():
|
|
615
615
|
# "httpupgrade CDN trojan",
|
616
616
|
"httpupgrade CDN vmess",
|
617
617
|
|
618
|
-
"
|
619
|
-
"
|
620
|
-
"
|
618
|
+
"xhttp CDN vless",
|
619
|
+
"xhttp CDN trojan",
|
620
|
+
"xhttp CDN vmess",
|
621
621
|
|
622
622
|
"grpc CDN vless",
|
623
623
|
"grpc CDN trojan",
|
@@ -663,7 +663,7 @@ def make_proxy_rows(cfgs):
|
|
663
663
|
for l3 in [ProxyL3.h3_quic, "tls_h2", "tls", "http", "reality"]:
|
664
664
|
for c in cfgs:
|
665
665
|
transport, cdn, proto = c.split(" ")
|
666
|
-
if transport != ProxyTransport.
|
666
|
+
if transport != ProxyTransport.xhttp and l3 == ProxyL3.h3_quic:
|
667
667
|
continue
|
668
668
|
if l3 in ["kcp", 'reality'] and cdn != "direct":
|
669
669
|
continue
|
@@ -750,8 +750,11 @@ def add_new_enum_values():
|
|
750
750
|
continue
|
751
751
|
|
752
752
|
# Add the new value to the enum column in the database
|
753
|
-
enumstr = ','.join([f"'{a}'" for a in [*existing_values, *old_values]])
|
754
|
-
|
753
|
+
# enumstr = ','.join([f"'{a}'" for a in [*existing_values, *old_values]])
|
754
|
+
enumstr = ','.join([f"'{a}'" for a in [*existing_values]])
|
755
|
+
expired_enumstr = ','.join([f"'{a}'" for a in [*old_values]])
|
756
|
+
db_execute(
|
757
|
+
f"delete from {table_name} where `{column_name}` in ({expired_enumstr});", commit=True)
|
755
758
|
db_execute(
|
756
759
|
f"ALTER TABLE {table_name} MODIFY COLUMN `{column_name}` ENUM({enumstr});", commit=True)
|
757
760
|
|
hiddifypanel/panel/user/user.py
CHANGED
@@ -86,27 +86,15 @@ class UserView(FlaskView):
|
|
86
86
|
continue
|
87
87
|
wireguards.append(pinfo)
|
88
88
|
|
89
|
-
|
89
|
+
|
90
90
|
|
91
91
|
if not len(wireguards):
|
92
92
|
abort(404)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
[Interface]
|
99
|
-
PrivateKey = {wg['wg_pk']}
|
100
|
-
Address = {addrs}
|
101
|
-
DNS = 1.1.1.1
|
102
|
-
MTU = 1390
|
103
|
-
|
104
|
-
[Peer]
|
105
|
-
PublicKey = {wg['wg_pub']}
|
106
|
-
PresharedKey = {wg['wg_psk']}
|
107
|
-
AllowedIPs = 0.0.0.0/1, 128.0.0.0/1, ::/1, 8000::/1
|
108
|
-
Endpoint = {next(iter(servers))}:61339 #{servers}
|
109
|
-
"""
|
93
|
+
resp =""
|
94
|
+
for wg in wireguards:
|
95
|
+
resp +=f'#========={wg["extra_info"]} {wg["name"]}================\n'
|
96
|
+
resp+=hutils.proxy.wireguard.generate_wireguard_config(wg)
|
97
|
+
resp+="\n\n"
|
110
98
|
return add_headers(resp, c)
|
111
99
|
|
112
100
|
# return self.singbox_ssh_imp()
|