hiddifypanel 10.30.8.dev0__py3-none-any.whl → 10.30.9.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/hutils/network/net.py +5 -3
- hiddifypanel/models/user.py +3 -2
- hiddifypanel/panel/admin/Actions.py +2 -2
- hiddifypanel/panel/admin/AdminstratorAdmin.py +1 -1
- hiddifypanel/panel/admin/Dashboard.py +3 -4
- hiddifypanel/panel/admin/DomainAdmin.py +3 -3
- hiddifypanel/panel/admin/QuickSetup.py +6 -5
- hiddifypanel/panel/admin/SettingAdmin.py +12 -12
- hiddifypanel/panel/admin/UserAdmin.py +7 -4
- hiddifypanel/panel/admin/templates/model/domain_list.html +13 -12
- hiddifypanel/panel/admin/templates/model/user_list.html +8 -5
- hiddifypanel/panel/admin/templates/quick_setup.html +1 -1
- hiddifypanel/panel/admin/templates/result.html +3 -2
- hiddifypanel/panel/commercial/ParentDomainAdmin.py +1 -1
- hiddifypanel/panel/commercial/restapi/v2/admin/schema.py +3 -0
- hiddifypanel/panel/commercial/telegrambot/Usage.py +1 -4
- hiddifypanel/panel/commercial/telegrambot/admin.py +3 -3
- hiddifypanel/panel/commercial/telegrambot/information.py +1 -3
- hiddifypanel/panel/common_bp/login.py +2 -2
- hiddifypanel/panel/user/templates/home/home.html +30 -14
- hiddifypanel/panel/user/templates/home/index_old.html +2 -2
- hiddifypanel/panel/user/templates/home/multi.html +2 -2
- hiddifypanel/panel/user/templates/redirect_to_new_format.html +1 -1
- hiddifypanel/templates/500.html +1 -1
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +112 -130
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +114 -133
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +104 -127
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +109 -132
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +93 -114
- hiddifypanel/translations.i18n/en.json +41 -71
- hiddifypanel/translations.i18n/fa.json +122 -73
- hiddifypanel/translations.i18n/pt.json +327 -69
- hiddifypanel/translations.i18n/ru.json +180 -74
- hiddifypanel/translations.i18n/zh.json +308 -69
- {hiddifypanel-10.30.8.dev0.dist-info → hiddifypanel-10.30.9.dev0.dist-info}/METADATA +1 -1
- {hiddifypanel-10.30.8.dev0.dist-info → hiddifypanel-10.30.9.dev0.dist-info}/RECORD +47 -47
- {hiddifypanel-10.30.8.dev0.dist-info → hiddifypanel-10.30.9.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.30.8.dev0.dist-info → hiddifypanel-10.30.9.dev0.dist-info}/WHEEL +0 -0
- {hiddifypanel-10.30.8.dev0.dist-info → hiddifypanel-10.30.9.dev0.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-10.30.8.dev0.dist-info → hiddifypanel-10.30.9.dev0.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
10.30.
|
1
|
+
10.30.9.dev0
|
hiddifypanel/VERSION.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
__version__='10.30.
|
1
|
+
__version__='10.30.9.dev0'
|
2
2
|
from datetime import datetime
|
3
|
-
__release_date__= datetime.strptime('2024-07-
|
3
|
+
__release_date__= datetime.strptime('2024-07-12','%Y-%m-%d')
|
@@ -136,6 +136,7 @@ def get_ip(version: Literal[4, 6], retry: int = 5) -> ipaddress.IPv4Address | ip
|
|
136
136
|
ip = get_ip(version, retry=retry - 1)
|
137
137
|
return ip
|
138
138
|
|
139
|
+
|
139
140
|
def get_random_domains(count: int = 1, retry: int = 3) -> List[str]:
|
140
141
|
try:
|
141
142
|
irurl = "https://api.ooni.io/api/v1/measurements?probe_cc=IR&test_name=web_connectivity&anomaly=false&confirmed=false&failure=false&order_by=test_start_time&limit=1000"
|
@@ -310,7 +311,7 @@ def is_in_same_asn(domain_or_ip: str, domain_or_ip_target: str) -> bool:
|
|
310
311
|
print(f"An error occurred: {e}")
|
311
312
|
return False
|
312
313
|
|
313
|
-
# hutils.flask.flash(_("
|
314
|
+
# hutils.flask.flash(_("domain.reality.asn_issue") +
|
314
315
|
# f"<br> Server ASN={asn_ipv4.get('autonomous_system_organization','unknown')}<br>{domain}_ASN={asn_dip.get('autonomous_system_organization','unknown')}", "warning")
|
315
316
|
|
316
317
|
|
@@ -342,8 +343,9 @@ def is_ip(input: str):
|
|
342
343
|
except:
|
343
344
|
return False
|
344
345
|
|
345
|
-
|
346
|
+
|
347
|
+
def resolve_domain_with_api(domain: str) -> str:
|
346
348
|
if not domain:
|
347
349
|
return ''
|
348
350
|
endpoint = f'http://ip-api.com/json/{domain}?fields=query'
|
349
|
-
return str(requests.get(endpoint).json().get('query'))
|
351
|
+
return str(requests.get(endpoint).json().get('query'))
|
hiddifypanel/models/user.py
CHANGED
@@ -290,7 +290,7 @@ class User(BaseAccount, SerializerMixin):
|
|
290
290
|
return schema.dump(User())
|
291
291
|
|
292
292
|
def to_schema(self):
|
293
|
-
user_dict = self.to_dict()
|
293
|
+
user_dict = self.to_dict(dump_id=True)
|
294
294
|
from hiddifypanel.panel.commercial.restapi.v2.admin.user_api import UserSchema
|
295
295
|
return UserSchema().load(user_dict)
|
296
296
|
|
@@ -317,7 +317,8 @@ class User(BaseAccount, SerializerMixin):
|
|
317
317
|
'wg_pk': self.wg_pk,
|
318
318
|
'wg_pub': self.wg_pub,
|
319
319
|
'wg_psk': self.wg_psk,
|
320
|
-
'is_active': self.is_active
|
320
|
+
'is_active': self.is_active,
|
321
|
+
'enable': self.enable
|
321
322
|
}
|
322
323
|
|
323
324
|
# @staticmethod
|
@@ -65,7 +65,7 @@ class Actions(FlaskView):
|
|
65
65
|
domain_changed = request.args.get("domain_changed", str(domain_changed)).lower() == "true"
|
66
66
|
complete_install = request.args.get("complete_install", str(complete_install)).lower() == "true"
|
67
67
|
if domain_changed:
|
68
|
-
hutils.flask.flash((_('
|
68
|
+
hutils.flask.flash((_('domain.changed_in_domain_warning')), 'info')
|
69
69
|
# hutils.flask.flash(f'complete_install={complete_install} domain_changed={domain_changed} ', 'info')
|
70
70
|
# return render_template("result.html")
|
71
71
|
# hiddify.add_temporary_access()
|
@@ -87,7 +87,7 @@ class Actions(FlaskView):
|
|
87
87
|
|
88
88
|
resp = render_template("result.html",
|
89
89
|
out_type="info",
|
90
|
-
out_msg=_("
|
90
|
+
out_msg=_("admin.waiting_for_update") +
|
91
91
|
admin_links,
|
92
92
|
log_file_url=get_log_api_url(),
|
93
93
|
log_file="0-install.log",
|
@@ -72,7 +72,7 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
72
72
|
|
73
73
|
column_descriptions = dict(
|
74
74
|
comment=_("Add some text that is only visible to super_admin."),
|
75
|
-
mode=_("
|
75
|
+
mode=_("admin.define_mode"),
|
76
76
|
)
|
77
77
|
# create_modal = True
|
78
78
|
can_export = False
|
@@ -22,7 +22,7 @@ class Dashboard(FlaskView):
|
|
22
22
|
return redirect(hurl_for("admin.QuickSetup:index"))
|
23
23
|
|
24
24
|
if hutils.utils.is_panel_outdated():
|
25
|
-
hutils.flask.flash(_('
|
25
|
+
hutils.flask.flash(_('outdated_panel'), "danger") # type: ignore
|
26
26
|
|
27
27
|
childs = None
|
28
28
|
admin_id = request.args.get("admin_id") or g.account.id
|
@@ -48,8 +48,7 @@ class Dashboard(FlaskView):
|
|
48
48
|
|
49
49
|
if def_user and sslip_domains:
|
50
50
|
quick_setup = hurl_for("admin.QuickSetup:index")
|
51
|
-
hutils.flask.flash((_('
|
52
|
-
quick_setup=quick_setup)), 'warning') # type: ignore
|
51
|
+
hutils.flask.flash((_('admin.incomplete_setup_warning', quick_setup=quick_setup)), 'warning') # type: ignore
|
53
52
|
if hutils.node.is_parent():
|
54
53
|
hutils.flask.flash(
|
55
54
|
_("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
|
@@ -61,7 +60,7 @@ class Dashboard(FlaskView):
|
|
61
60
|
_("Please understand that parent panel is under test and the plan and the condition of use maybe change at anytime."), "danger") # type: ignore
|
62
61
|
elif def_user:
|
63
62
|
d = domains[0]
|
64
|
-
hutils.flask.flash((_(
|
63
|
+
hutils.flask.flash((_("admin.no_user_warning",
|
65
64
|
default_link=hiddify.get_html_user_link(def_user, d))), 'secondary') # type: ignore
|
66
65
|
if hutils.network.is_ssh_password_authentication_enabled():
|
67
66
|
hutils.flask.flash(_('serverssh.password-login.warning'), "warning") # type: ignore
|
@@ -46,11 +46,11 @@ class DomainAdmin(AdminLTEModelView):
|
|
46
46
|
mode=_("Direct mode means you want to use your server directly (for usual use), CDN means that you use your server on behind of a CDN provider."),
|
47
47
|
cdn_ip=_("config.cdn_forced_host.description"),
|
48
48
|
show_domains=_(
|
49
|
-
'
|
49
|
+
'domain.show_domains_description'),
|
50
50
|
alias=_('The name shown in the configs for this domain.'),
|
51
51
|
servernames=_('config.reality_server_names.description'),
|
52
52
|
sub_link_only=_('This can be used for giving your users a permanent non blockable links.'),
|
53
|
-
grpc=_('
|
53
|
+
grpc=_('grpc-proxy.description'))
|
54
54
|
|
55
55
|
# create_modal = True
|
56
56
|
can_export = False
|
@@ -243,7 +243,7 @@ class DomainAdmin(AdminLTEModelView):
|
|
243
243
|
if not hutils.network.is_in_same_asn(d, ipv4_list[0]):
|
244
244
|
server_asn = hutils.network.get_ip_asn(ipv4_list[0])
|
245
245
|
domain_asn = hutils.network.get_ip_asn(dip) # type: ignore
|
246
|
-
msg = _("
|
246
|
+
msg = _("domain.reality.asn_issue") + \
|
247
247
|
(f"<br> Server ASN={server_asn}<br>{d}_ASN={domain_asn}" if server_asn or domain_asn else "")
|
248
248
|
hutils.flask.flash(msg, 'warning')
|
249
249
|
|
@@ -112,10 +112,10 @@ def get_quick_setup_form(empty=False):
|
|
112
112
|
domain_regex = "^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})$"
|
113
113
|
|
114
114
|
domain_validators = [
|
115
|
-
wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.
|
115
|
+
wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.Invalid_domain")),
|
116
116
|
validate_domain,
|
117
|
-
wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.
|
118
|
-
wtf.validators.NoneOf([c.value.lower() for c in StrConfig.query.all() if "fakedomain" in c.key and c.key != ConfigEnum.decoy_domain], _("config.
|
117
|
+
wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.Domain_already_used")),
|
118
|
+
wtf.validators.NoneOf([c.value.lower() for c in StrConfig.query.all() if "fakedomain" in c.key and c.key != ConfigEnum.decoy_domain], _("config.Domain_already_used"))]
|
119
119
|
domain = wtf.StringField(
|
120
120
|
_("domain.domain"),
|
121
121
|
domain_validators,
|
@@ -132,7 +132,7 @@ def get_quick_setup_form(empty=False):
|
|
132
132
|
"config.block_iran_sites.description"), default=hconfig(ConfigEnum.block_iran_sites))
|
133
133
|
# enable_vmess = SwitchField(_("config.vmess_enable.label"), description=_("config.vmess_enable.description"), default=hconfig(ConfigEnum.vmess_enable))
|
134
134
|
decoy_domain = wtf.StringField(_("config.decoy_domain.label"), description=_("config.decoy_domain.description"), default=hconfig(
|
135
|
-
ConfigEnum.decoy_domain), validators=[wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.
|
135
|
+
ConfigEnum.decoy_domain), validators=[wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.Invalid_domain")), hutils.flask.validate_domain_exist])
|
136
136
|
submit = wtf.SubmitField(_('Submit'))
|
137
137
|
|
138
138
|
return QuickSetupForm(None) if empty else QuickSetupForm()
|
@@ -146,7 +146,8 @@ def validate_domain(form, field):
|
|
146
146
|
|
147
147
|
myip = hutils.network.get_ip(4)
|
148
148
|
if dip and myip != dip:
|
149
|
-
raise ValidationError(_("Domain (%(domain)s)-> IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode",
|
149
|
+
raise ValidationError(_("Domain (%(domain)s)-> IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode",
|
150
|
+
server_ip=myip, domain_ip=dip, domain=domain))
|
150
151
|
|
151
152
|
|
152
153
|
def admin_link():
|
@@ -267,16 +267,16 @@ def get_config_form():
|
|
267
267
|
render_kw = {'class': "ltr"}
|
268
268
|
validators = []
|
269
269
|
if c.key == ConfigEnum.domain_fronting_domain:
|
270
|
-
validators.append(wtf.validators.Regexp("^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})|$", re.IGNORECASE, _("config.
|
270
|
+
validators.append(wtf.validators.Regexp("^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})|$", re.IGNORECASE, _("config.Invalid_domain")))
|
271
271
|
validators.append(hutils.flask.validate_domain_exist)
|
272
272
|
elif '_domain' in c.key or "_fakedomain" in c.key:
|
273
|
-
validators.append(wtf.validators.Regexp("^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})$", re.IGNORECASE, _("config.
|
273
|
+
validators.append(wtf.validators.Regexp("^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})$", re.IGNORECASE, _("config.Invalid_domain")))
|
274
274
|
validators.append(hutils.flask.validate_domain_exist)
|
275
275
|
|
276
276
|
if c.key != ConfigEnum.decoy_domain:
|
277
|
-
validators.append(wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.
|
277
|
+
validators.append(wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.Domain_already_used")))
|
278
278
|
validators.append(wtf.validators.NoneOf(
|
279
|
-
[cc.value.lower() for cc in StrConfig.query.filter(StrConfig.child_id == Child.current().id).all() if cc.key != c.key and "fakedomain" in cc.key and cc.key != ConfigEnum.decoy_domain], _("config.
|
279
|
+
[cc.value.lower() for cc in StrConfig.query.filter(StrConfig.child_id == Child.current().id).all() if cc.key != c.key and "fakedomain" in cc.key and cc.key != ConfigEnum.decoy_domain], _("config.Domain_already_used")))
|
280
280
|
|
281
281
|
render_kw['required'] = ""
|
282
282
|
if len(c.value) < 3:
|
@@ -292,36 +292,36 @@ def get_config_form():
|
|
292
292
|
if c.key == ConfigEnum.parent_panel:
|
293
293
|
validators.append(wtf.validators.Regexp("()|(http(s|)://([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})/.*)", re.IGNORECASE, _("Invalid admin link")))
|
294
294
|
if c.key == ConfigEnum.telegram_bot_token:
|
295
|
-
validators.append(wtf.validators.Regexp("()|^([0-9]{8,12}:[a-zA-Z0-9_-]{30,40})|$", re.IGNORECASE, _("config.
|
295
|
+
validators.append(wtf.validators.Regexp("()|^([0-9]{8,12}:[a-zA-Z0-9_-]{30,40})|$", re.IGNORECASE, _("config.Invalid_telegram_bot_token")))
|
296
296
|
if c.key == ConfigEnum.branding_site:
|
297
297
|
validators.append(wtf.validators.Regexp(
|
298
|
-
"()|(http(s|)://([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})/?.*)", re.IGNORECASE, _("config.
|
298
|
+
"()|(http(s|)://([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})/?.*)", re.IGNORECASE, _("config.Invalid_brand_link")))
|
299
299
|
# render_kw['required']=""
|
300
300
|
|
301
301
|
if 'secret' in c.key:
|
302
302
|
validators.append(wtf.validators.Regexp(
|
303
|
-
"^[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}$", re.IGNORECASE, _('config.
|
303
|
+
"^[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}$", re.IGNORECASE, _('config.invalid_uuid')))
|
304
304
|
render_kw['required'] = ""
|
305
305
|
|
306
306
|
if c.key == ConfigEnum.proxy_path:
|
307
|
-
validators.append(wtf.validators.Regexp("^[a-zA-Z0-9]*$", re.IGNORECASE, _("config.
|
307
|
+
validators.append(wtf.validators.Regexp("^[a-zA-Z0-9]*$", re.IGNORECASE, _("config.Invalid_proxy_path")))
|
308
308
|
render_kw['required'] = ""
|
309
309
|
|
310
310
|
if 'port' in c.key:
|
311
311
|
if c.key in [ConfigEnum.http_ports, ConfigEnum.tls_ports]:
|
312
|
-
validators.append(wtf.validators.Regexp("^(\\d+)(,\\d+)*$", re.IGNORECASE, _("config.
|
312
|
+
validators.append(wtf.validators.Regexp("^(\\d+)(,\\d+)*$", re.IGNORECASE, _("config.Invalid_port")))
|
313
313
|
render_kw['required'] = ""
|
314
314
|
else:
|
315
|
-
validators.append(wtf.validators.Regexp("^(\\d+)(,\\d+)*$|^$", re.IGNORECASE, _("config.
|
315
|
+
validators.append(wtf.validators.Regexp("^(\\d+)(,\\d+)*$|^$", re.IGNORECASE, _("config.Invalid_port")))
|
316
316
|
# validators.append(wtf.validators.Regexp("^(\d+)(,\d+)*$",re.IGNORECASE,_("config.port is required")))
|
317
317
|
|
318
318
|
# tls tricks validations
|
319
319
|
if c.key in [ConfigEnum.tls_fragment_size, ConfigEnum.tls_fragment_sleep, ConfigEnum.tls_padding_length, ConfigEnum.wireguard_noise_trick]:
|
320
|
-
validators.append(wtf.validators.Regexp("^\\d+-\\d+$", re.IGNORECASE, _("config.
|
320
|
+
validators.append(wtf.validators.Regexp("^\\d+-\\d+$", re.IGNORECASE, _("config.Invalid_The_pattern_is_number-number") + f' {c.key}'))
|
321
321
|
# mux and hysteria validations
|
322
322
|
if c.key in [ConfigEnum.hysteria_up_mbps, ConfigEnum.hysteria_down_mbps, ConfigEnum.mux_max_connections, ConfigEnum.mux_min_streams, ConfigEnum.mux_max_streams,
|
323
323
|
ConfigEnum.mux_brutal_down_mbps, ConfigEnum.mux_brutal_up_mbps]:
|
324
|
-
validators.append(wtf.validators.Regexp("^\\d+$", re.IGNORECASE, _("config.
|
324
|
+
validators.append(wtf.validators.Regexp("^\\d+$", re.IGNORECASE, _("config.Invalid_it_should_be_a_number_only") + f' {c.key}'))
|
325
325
|
|
326
326
|
for val in validators:
|
327
327
|
if hasattr(val, "regex"):
|
@@ -10,7 +10,7 @@ from wtforms.validators import NumberRange
|
|
10
10
|
from flask_babel import lazy_gettext as _
|
11
11
|
from flask import g, request # type: ignore
|
12
12
|
from markupsafe import Markup
|
13
|
-
from sqlalchemy import desc
|
13
|
+
from sqlalchemy import desc, func
|
14
14
|
|
15
15
|
from hiddifypanel.hutils.flask import hurl_for
|
16
16
|
from wtforms.validators import Regexp, ValidationError
|
@@ -25,6 +25,7 @@ from hiddifypanel import hutils
|
|
25
25
|
|
26
26
|
|
27
27
|
class UserAdmin(AdminLTEModelView):
|
28
|
+
column_default_sort = ('id', False) # Sort by username in ascending order
|
28
29
|
|
29
30
|
column_sortable_list = ["is_active", "name", "current_usage", 'mode', "remaining_days", "comment", 'last_online', "uuid", 'remaining_days']
|
30
31
|
column_searchable_list = ["uuid", "name"]
|
@@ -112,7 +113,7 @@ class UserAdmin(AdminLTEModelView):
|
|
112
113
|
# usage_limit_GB="in GB",
|
113
114
|
# current_usage_GB="in GB"
|
114
115
|
comment=_("Add some text that is only visible to you."),
|
115
|
-
mode=_("
|
116
|
+
mode=_("user.define_mode"),
|
116
117
|
last_reset_time=_("If monthly is enabled, the usage will be reset after 30 days from this date."),
|
117
118
|
start_date=_("From when the user package will be started? Empty for start from first connection"),
|
118
119
|
package_days=_("How many days this package should be available?")
|
@@ -376,15 +377,17 @@ class UserAdmin(AdminLTEModelView):
|
|
376
377
|
abort(403)
|
377
378
|
|
378
379
|
query = query.filter(User.added_by.in_(admin.recursive_sub_admins_ids()))
|
379
|
-
|
380
|
+
|
380
381
|
return query
|
381
382
|
|
382
383
|
# Override get_count_query() to include the filter condition in the count query
|
383
384
|
def get_count_query(self):
|
384
385
|
# Get the base count query
|
385
386
|
|
386
|
-
# query =
|
387
|
+
# query = self.session.query(func.count(User.id)).
|
388
|
+
|
387
389
|
query = super().get_count_query()
|
390
|
+
|
388
391
|
admin_id = int(request.args.get("admin_id") or g.account.id)
|
389
392
|
if admin_id not in g.account.recursive_sub_admins_ids():
|
390
393
|
abort(403)
|
@@ -1,19 +1,20 @@
|
|
1
1
|
{% extends 'hiddify-flask-admin/list.html' %}
|
2
2
|
|
3
3
|
{% block body %}
|
4
|
-
|
4
|
+
{{super()}}
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
<div class="callout callout-info">
|
7
|
+
{{_('domain.intro')}}
|
8
|
+
</div>
|
9
9
|
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
<style>
|
12
|
+
.table td {
|
13
|
+
vertical-align: middle;
|
14
|
+
}
|
15
|
+
|
16
|
+
td.col-domain {
|
17
|
+
/* direction:ltr; */
|
18
|
+
}
|
19
|
+
</style>
|
19
20
|
{% endblock %}
|
@@ -11,7 +11,8 @@
|
|
11
11
|
<button onclick="show_send_message('all')" type="button" class="btn hbtn bg-h-green">
|
12
12
|
{{_("All Users")}}
|
13
13
|
</button>
|
14
|
-
<button onclick="show_send_message('active')" type="button" class="btn hbtn bg-h-green"
|
14
|
+
<button onclick="show_send_message('active')" type="button" class="btn hbtn bg-h-green"
|
15
|
+
style="margin-left: 5px;margin-right: 5px;">
|
15
16
|
{{_("Active Users")}}
|
16
17
|
</button>
|
17
18
|
<button onclick="show_send_message('selected')" type="button" class="btn hbtn bg-h-green">
|
@@ -22,9 +23,11 @@
|
|
22
23
|
<div style="margin-top: 10px;">
|
23
24
|
<button onclick="show_send_message('offline 1h')" type="button" class="btn hbtn bg-h-red">
|
24
25
|
{{_("Offline more than 1 hour")}}</button>
|
25
|
-
<button onclick="show_send_message('offline 1d')" type="button" class="btn hbtn bg-h-red"
|
26
|
+
<button onclick="show_send_message('offline 1d')" type="button" class="btn hbtn bg-h-red"
|
27
|
+
style="margin-left: 5px;">
|
26
28
|
{{_("Offline more than 1 day")}}</button>
|
27
|
-
<button onclick="show_send_message('offline 1w')" type="button" class="btn hbtn bg-h-red"
|
29
|
+
<button onclick="show_send_message('offline 1w')" type="button" class="btn hbtn bg-h-red"
|
30
|
+
style="margin-left: 5px;margin-right: 5px;">
|
28
31
|
{{_("Offline more than 1 week")}}
|
29
32
|
</button>
|
30
33
|
<button onclick="show_send_message('expired')" type="button" class="btn hbtn bg-h-red">
|
@@ -37,7 +40,7 @@
|
|
37
40
|
{{super()}}
|
38
41
|
|
39
42
|
<div class="callout callout-success">
|
40
|
-
{{_('
|
43
|
+
{{_('adminuser.force_update_usage',link=hurl_for("admin.Actions:update_usage"))}}
|
41
44
|
|
42
45
|
</div>
|
43
46
|
<style>
|
@@ -155,4 +158,4 @@
|
|
155
158
|
}
|
156
159
|
|
157
160
|
</script>
|
158
|
-
{% endblock %}
|
161
|
+
{% endblock %}
|
@@ -5,7 +5,7 @@
|
|
5
5
|
{% block tail %}
|
6
6
|
{{super()}}
|
7
7
|
|
8
|
-
{{modal('quicksetup',_('Save Link'),_('
|
8
|
+
{{modal('quicksetup',_('Save Link'),_('admin.save_panel_link',adminlink=admin_link),show=Fales)}}
|
9
9
|
|
10
10
|
<script>
|
11
11
|
disable_notif = false
|
@@ -40,7 +40,8 @@ page."),temp_link(),show=False)}}
|
|
40
40
|
<label class="col-6" id="process-title"></label> <label class="col-6" id="process-details"></label>
|
41
41
|
</div>
|
42
42
|
<div class="progress">
|
43
|
-
<div id="progress" class="progress-bar" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0"
|
43
|
+
<div id="progress" class="progress-bar" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0"
|
44
|
+
aria-valuemax="100"></div>
|
44
45
|
</div>
|
45
46
|
</div>
|
46
47
|
{% endif %}
|
@@ -153,7 +154,7 @@ page."),temp_link(),show=False)}}
|
|
153
154
|
$("#process-title").html("")
|
154
155
|
bootbox.alert({
|
155
156
|
title: '{{_("Success")}}',
|
156
|
-
message: '{{_("
|
157
|
+
message: '{{_("admin.action_done_sucess")}}',
|
157
158
|
locale: '{{get_locale()}}',
|
158
159
|
});
|
159
160
|
}
|
@@ -25,7 +25,7 @@
|
|
25
25
|
# column_descriptions = dict(
|
26
26
|
# domain=_("domain.description"),
|
27
27
|
# alias=_('The name shown in the configs for this domain.'),
|
28
|
-
# show_domains=_('
|
28
|
+
# show_domains=_('domain.show_domains_description')
|
29
29
|
# # current_usage_GB="in GB"
|
30
30
|
# )
|
31
31
|
|
@@ -129,6 +129,7 @@ class UserSchema(Schema):
|
|
129
129
|
lang = Enum(Lang, required=False, allow_none=True, description="The language of the user")
|
130
130
|
enable = Boolean(required=False, description="Whether the user is enabled or not")
|
131
131
|
is_active = Boolean(required=False, description="Whether the user is active for using hiddify")
|
132
|
+
id = Integer(required=False, description="never use it, only for better presentation")
|
132
133
|
|
133
134
|
|
134
135
|
class PostUserSchema(UserSchema):
|
@@ -137,6 +138,7 @@ class PostUserSchema(UserSchema):
|
|
137
138
|
# the uuid is sent in the url path
|
138
139
|
self.fields['uuid'].required = False
|
139
140
|
self.fields['uuid'].allow_none = True
|
141
|
+
del self.fields['id']
|
140
142
|
|
141
143
|
|
142
144
|
class PatchUserSchema(UserSchema):
|
@@ -146,6 +148,7 @@ class PatchUserSchema(UserSchema):
|
|
146
148
|
self.fields['uuid'].allow_none = True,
|
147
149
|
self.fields['name'].required = False
|
148
150
|
self.fields['name'].allow_none = True,
|
151
|
+
del self.fields['id']
|
149
152
|
|
150
153
|
|
151
154
|
# endregion
|
@@ -30,10 +30,7 @@ def send_welcome(message):
|
|
30
30
|
with force_locale(user.lang or hconfig(ConfigEnum.lang)):
|
31
31
|
bot.reply_to(message, get_usage_msg(user.uuid), reply_markup=user_keyboard(user.uuid))
|
32
32
|
else:
|
33
|
-
bot.reply_to(message,
|
34
|
-
_("Hooray \U0001F389 \U0001F389 \U0001F389 \n"
|
35
|
-
"Welcome to hiddifybot.\n"
|
36
|
-
"Start by clicking the link on the panel or entering your UUID."))
|
33
|
+
bot.reply_to(message, _("bot.welcome"))
|
37
34
|
|
38
35
|
|
39
36
|
def user_keyboard(uuid):
|
@@ -32,7 +32,7 @@ def send_welcome(message):
|
|
32
32
|
|
33
33
|
def start_admin(message):
|
34
34
|
|
35
|
-
bot.reply_to(message, _("
|
35
|
+
bot.reply_to(message, _("bot.admin_welcome"), reply_markup=admin_keyboard_main())
|
36
36
|
|
37
37
|
|
38
38
|
def get_admin_by_tgid(message):
|
@@ -136,14 +136,14 @@ def create_package(call): # <- passes a CallbackQuery type object to your funct
|
|
136
136
|
days = int(splt[2])
|
137
137
|
count = int(splt[3])
|
138
138
|
domain = int(splt[4])
|
139
|
-
new_text = _("Please Wait
|
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
141
|
domain = Domain.query.filter(Domain.id == domain).first()
|
142
142
|
from . import Usage
|
143
143
|
admin_id = admin.id
|
144
144
|
admin_name = admin.name
|
145
145
|
for i in range(1, count + 1):
|
146
|
-
new_text = _("Please Wait
|
146
|
+
new_text = _("Please Wait") + f' {i}/{count}'
|
147
147
|
bot.edit_message_text(new_text, call.message.chat.id, call.message.message_id, reply_markup=None)
|
148
148
|
user = User(package_days=days, usage_limit_GB=gig, name=f"{admin_name} auto {i} {datetime.date.today()}", added_by=admin_id)
|
149
149
|
db.session.add(user)
|
@@ -52,9 +52,7 @@ def prepare_hello_message():
|
|
52
52
|
@return: A text message
|
53
53
|
"""
|
54
54
|
|
55
|
-
response = _("
|
56
|
-
"Welcome to hiddifybot.\n"
|
57
|
-
"Start by clicking the link on the panel or entering your UUID.")
|
55
|
+
response = _("bot.welcome")
|
58
56
|
|
59
57
|
return response
|
60
58
|
|
@@ -16,11 +16,11 @@ import re
|
|
16
16
|
|
17
17
|
class LoginForm(FlaskForm):
|
18
18
|
secret_textbox = wtf.fields.StringField(_(f'login.secret.label'), [wtf.validators.Regexp(
|
19
|
-
"^[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}$", re.IGNORECASE, _('config.
|
19
|
+
"^[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}$", re.IGNORECASE, _('config.invalid_uuid'))], default='',
|
20
20
|
description=_(f'login.secret.description'), render_kw={
|
21
21
|
'required': "",
|
22
22
|
'pattern': "^[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}$",
|
23
|
-
'message': _('config.
|
23
|
+
'message': _('config.invalid_uuid')
|
24
24
|
})
|
25
25
|
submit = wtf.fields.SubmitField(_('Submit'))
|
26
26
|
|
@@ -19,8 +19,11 @@
|
|
19
19
|
<i class="fa-solid fa-moon"></i>
|
20
20
|
</a>
|
21
21
|
{% endif %}
|
22
|
-
<button class='btn btn-sm btn-default' onclick='show_video("features")'><i
|
23
|
-
|
22
|
+
<button class='btn btn-sm btn-default' onclick='show_video("features")'><i
|
23
|
+
class="fa-solid fa-question"></i></button> <a
|
24
|
+
href="{{'https://github.com/hiddify/hiddify-manager/wiki'}}">
|
25
|
+
{{hconfig(ConfigEnum.branding_title) or _('user.home.title')}}</a>: <div class="d-inline-flex">
|
26
|
+
{{_('Welcome %(user)s',user=user.name if user.name!="default" else "")}}</div>
|
24
27
|
</h1>
|
25
28
|
|
26
29
|
|
@@ -48,19 +51,27 @@
|
|
48
51
|
<h3 class="card-title p-1"> </h3>
|
49
52
|
<ul class="nav nav-pills p-2">
|
50
53
|
{% if telegram_enable %}
|
51
|
-
<li class="nav-item"><a class="nav-link" href="#telegram" data-toggle="tab"><i
|
54
|
+
<li class="nav-item"><a class="nav-link" href="#telegram" data-toggle="tab"><i
|
55
|
+
class="fa-brands fa-telegram fa-margin"></i> {{_('telegram')}}</a></li>
|
52
56
|
{% endif %}
|
53
|
-
<li class="nav-item"><a class="nav-link {{" active" if ua.os.family=="Android" else "" }}"
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
<li class="nav-item"><a class="nav-link {{" active" if ua.os.family=="Android" else "" }}"
|
58
|
+
href="#android" data-toggle="tab"><i class="fa-brands fa-android fa-margin"></i>
|
59
|
+
{{_('android')}}</a></li>
|
60
|
+
<li class="nav-item"><a class="nav-link {{" active" if ua.os.family=="iOS" else "" }}" href="#ios"
|
61
|
+
data-toggle="tab"><i class="fa-brands fa-app-store-ios fa-margin"></i> {{_('ios')}}</a></li>
|
62
|
+
<li class="nav-item"><a class="nav-link {{" active" if ua.is_pc else "" }}" href="#windows"
|
63
|
+
data-toggle="tab">
|
64
|
+
<i class="fa-brands fa-windows fa-margin1"></i> <i
|
65
|
+
class="fa-brands fa-apple fa-margin1"></i> <i class="fa-brands fa-linux fa-margin1"></i>
|
66
|
+
{{_('desktop')}}</a></li>
|
57
67
|
|
58
68
|
</ul>
|
59
69
|
</div><!-- /.card-header -->
|
60
70
|
<div class="card-body">
|
61
71
|
<div class="tab-content">
|
62
|
-
<div class="tab-pane {{" active" if not ua.is_pc and ua.os.family not in ["Android", "iOS" ] else ""
|
63
|
-
|
72
|
+
<div class="tab-pane {{" active" if not ua.is_pc and ua.os.family not in ["Android", "iOS" ] else ""
|
73
|
+
}}" id="default">
|
74
|
+
{{_('user.home.select_os')}}
|
64
75
|
</div>
|
65
76
|
<div class="tab-pane" id="telegram">
|
66
77
|
{% include 'home/telegram.html' %}
|
@@ -94,15 +105,19 @@
|
|
94
105
|
<div class="card-header d-flex p-0">
|
95
106
|
<h3 class="card-title p-0"></h3>
|
96
107
|
<ul class="nav nav-pills p-2">
|
97
|
-
<li class="nav-item"><a class="nav-link" href="#all_configs" data-toggle="tab"><i
|
98
|
-
|
99
|
-
<li class="nav-item"><a class="nav-link" href="#
|
108
|
+
<li class="nav-item"><a class="nav-link" href="#all_configs" data-toggle="tab"><i
|
109
|
+
class="fa-solid fa-grip fa-margin"></i> {{_('user.home.all_configs')}}</a></li>
|
110
|
+
<li class="nav-item"><a class="nav-link" href="#speedtest" data-toggle="tab"><i
|
111
|
+
class="fa-solid fa-gauge-high fa-margin"></i> {{_('user.home.speedtest.title')}}</a>
|
112
|
+
</li>
|
113
|
+
<li class="nav-item"><a class="nav-link" href="#doh" data-toggle="tab"><i
|
114
|
+
class="fa-solid fa-sitemap fa-margin"></i> {{_('user.home.doh')}}</a></li>
|
100
115
|
</ul>
|
101
116
|
</div><!-- /.card-header -->
|
102
117
|
<div class="card-body">
|
103
118
|
<div class="tab-content">
|
104
119
|
<div class="tab-pane active" id="default">
|
105
|
-
{{_('user.home.
|
120
|
+
{{_('user.home.select_tool')}}
|
106
121
|
</div>
|
107
122
|
<div class="tab-pane" id="all_configs">
|
108
123
|
{% include 'home/all-configs.html'%}
|
@@ -135,7 +150,8 @@
|
|
135
150
|
<i class="fa-solid fa-qrcode fa-margin"></i>
|
136
151
|
</div>
|
137
152
|
</div>
|
138
|
-
<a href="https://{{domain}}/{{hconfigs[ConfigEnum.proxy_path]}}/{{user.uuid}}/"
|
153
|
+
<a href="https://{{domain}}/{{hconfigs[ConfigEnum.proxy_path]}}/{{user.uuid}}/"
|
154
|
+
class="card-footer share-link">
|
139
155
|
<i class="fas fa-qrcode"></i> {{_('user.home.qr-code.display')}}
|
140
156
|
</a>
|
141
157
|
|
@@ -60,7 +60,7 @@
|
|
60
60
|
<div class="tab-content">
|
61
61
|
<div class="tab-pane {{" active" if not ua.is_pc and ua.os.family not in ["Android", "iOS" ] else ""
|
62
62
|
}}" id="default">
|
63
|
-
{{_('user.home.
|
63
|
+
{{_('user.home.select_os')}}
|
64
64
|
</div>
|
65
65
|
<div class="tab-pane" id="telegram">
|
66
66
|
{% include 'home/telegram.html' %}
|
@@ -106,7 +106,7 @@
|
|
106
106
|
<div class="card-body">
|
107
107
|
<div class="tab-content">
|
108
108
|
<div class="tab-pane active" id="default">
|
109
|
-
{{_('user.home.
|
109
|
+
{{_('user.home.select_tool')}}
|
110
110
|
</div>
|
111
111
|
<div class="tab-pane" id="all_configs">
|
112
112
|
{% include 'home/all-configs.html'%}
|
@@ -73,7 +73,7 @@
|
|
73
73
|
<div class="tab-content">
|
74
74
|
<div class="tab-pane {{" active" if not ua.is_pc and ua.os.family not in ["Android", "iOS" ] else ""
|
75
75
|
}}" id="default">
|
76
|
-
{{_('user.home.
|
76
|
+
{{_('user.home.select_os')}}
|
77
77
|
</div>
|
78
78
|
<div class="tab-pane" id="telegram">
|
79
79
|
{% include 'home/telegram.html' %}
|
@@ -121,7 +121,7 @@
|
|
121
121
|
<div class="card-body">
|
122
122
|
<div class="tab-content">
|
123
123
|
<div class="tab-pane active" id="default">
|
124
|
-
{{_('user.home.
|
124
|
+
{{_('user.home.select_tool')}}
|
125
125
|
</div>
|
126
126
|
|
127
127
|
<div class="tab-pane" id="speedtest">
|