hiddifypanel 10.30.8.dev1__py3-none-any.whl → 10.30.9.dev1__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/cache.py +13 -3
- hiddifypanel/hutils/network/net.py +41 -10
- hiddifypanel/hutils/proxy/shared.py +1 -1
- hiddifypanel/hutils/proxy/xrayjson.py +6 -6
- hiddifypanel/models/config_enum.py +2 -2
- hiddifypanel/models/domain.py +1 -1
- 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 +48 -35
- hiddifypanel/panel/admin/ProxyAdmin.py +1 -1
- hiddifypanel/panel/admin/QuickSetup.py +144 -52
- hiddifypanel/panel/admin/SettingAdmin.py +23 -18
- 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 +20 -14
- 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/init_db.py +6 -6
- 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 +128 -133
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +130 -136
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +115 -129
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +120 -134
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +104 -116
- hiddifypanel/translations.i18n/en.json +52 -73
- hiddifypanel/translations.i18n/fa.json +133 -75
- hiddifypanel/translations.i18n/pt.json +337 -70
- hiddifypanel/translations.i18n/ru.json +190 -75
- hiddifypanel/translations.i18n/zh.json +318 -70
- {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/METADATA +1 -1
- {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/RECORD +54 -54
- {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/WHEEL +0 -0
- {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-10.30.8.dev1.dist-info → hiddifypanel-10.30.9.dev1.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ import uuid
|
|
4
4
|
# from flask_babelex import lazy_gettext as _
|
5
5
|
from flask import render_template, g, request
|
6
6
|
from flask_babel import gettext as _
|
7
|
+
from markupsafe import Markup
|
7
8
|
import wtforms as wtf
|
8
9
|
from flask_wtf import FlaskForm
|
9
10
|
from flask_bootstrap import SwitchField
|
@@ -22,67 +23,46 @@ from hiddifypanel.models import *
|
|
22
23
|
class QuickSetup(FlaskView):
|
23
24
|
decorators = [login_required({Role.super_admin})]
|
24
25
|
|
26
|
+
def current_form(self, step=None, empty=False, next=False):
|
27
|
+
step = int(step or request.form.get("step") or request.args.get('step', "1"))
|
28
|
+
if next:
|
29
|
+
step = step + 1
|
30
|
+
form = {1: get_lang_form,
|
31
|
+
2: get_quick_setup_form,
|
32
|
+
3: get_proxy_form}
|
33
|
+
|
34
|
+
return form[step](empty=empty or next)
|
35
|
+
|
25
36
|
def index(self):
|
26
37
|
return render_template(
|
27
|
-
'quick_setup.html',
|
28
|
-
form=
|
29
|
-
ipv4=hutils.network.get_ip_str(4),
|
30
|
-
ipv6=hutils.network.get_ip_str(6),
|
38
|
+
'quick_setup.html',
|
39
|
+
form=self.current_form(),
|
40
|
+
# ipv4=hutils.network.get_ip_str(4),
|
41
|
+
# ipv6=hutils.network.get_ip_str(6),
|
31
42
|
admin_link=admin_link(),
|
32
43
|
show_domain_info=True)
|
33
44
|
|
34
45
|
def post(self):
|
35
46
|
if request.args.get('changepw') == "true":
|
36
47
|
AdminUser.current_admin_or_owner().uuid = str(uuid.uuid4())
|
37
|
-
|
38
|
-
quick_form = get_quick_setup_form()
|
39
|
-
lang_form = get_lang_form()
|
40
|
-
if lang_form.lang_submit.data:
|
41
|
-
if lang_form.validate_on_submit():
|
42
|
-
set_hconfig(ConfigEnum.lang, lang_form.admin_lang.data)
|
43
|
-
set_hconfig(ConfigEnum.admin_lang, lang_form.admin_lang.data)
|
44
|
-
set_hconfig(ConfigEnum.country, lang_form.country.data)
|
45
|
-
|
46
|
-
flask_babel.refresh()
|
47
|
-
hutils.flask.flash((_('quicksetup.setlang.success')), 'success')
|
48
|
-
else:
|
49
|
-
hutils.flask.flash((_('quicksetup.setlang.error')), 'danger')
|
48
|
+
db.session.commit()
|
50
49
|
|
50
|
+
set_hconfig(ConfigEnum.first_setup, False)
|
51
|
+
form = self.current_form()
|
52
|
+
if not form.validate_on_submit() or form.step.data not in ["1", "2", "3"]:
|
53
|
+
hutils.flask.flash(_('config.validation-error'), 'danger')
|
51
54
|
return render_template(
|
52
|
-
'quick_setup.html', form=
|
53
|
-
lang_form=get_lang_form(),
|
55
|
+
'quick_setup.html', form=form,
|
54
56
|
admin_link=admin_link(),
|
55
57
|
ipv4=hutils.network.get_ip_str(4),
|
56
58
|
ipv6=hutils.network.get_ip_str(6),
|
57
59
|
show_domain_info=False)
|
58
|
-
|
59
|
-
if quick_form.validate_on_submit():
|
60
|
-
Domain.query.filter(Domain.domain == f'{hutils.network.get_ip_str(4)}.sslip.io').delete()
|
61
|
-
db.session.add(Domain(domain=quick_form.domain.data.lower(), mode=DomainType.direct))
|
62
|
-
set_hconfig(ConfigEnum.block_iran_sites, quick_form.block_iran_sites.data)
|
63
|
-
set_hconfig(ConfigEnum.decoy_domain, quick_form.decoy_domain.data)
|
64
|
-
# hiddify.bulk_register_configs([
|
65
|
-
# # {"key": ConfigEnum.telegram_enable, "value": quick_form.enable_telegram.data == True},
|
66
|
-
# # {"key": ConfigEnum.vmess_enable, "value": quick_form.enable_vmess.data == True},
|
67
|
-
# # {"key": ConfigEnum.firewall, "value": quick_form.enable_firewall.data == True},
|
68
|
-
# {"key": ConfigEnum.block_iran_sites, "value": quick_form.block_iran_sites.data == True},
|
69
|
-
# # {"key":ConfigEnum.decoy_domain,"value":quick_form.decoy_domain.data}
|
70
|
-
# ])
|
71
|
-
|
72
|
-
from .Actions import Actions
|
73
|
-
return Actions().reinstall(domain_changed=True)
|
74
|
-
else:
|
75
|
-
hutils.flask.flash(_('config.validation-error'), 'danger')
|
76
|
-
return render_template(
|
77
|
-
'quick_setup.html', form=quick_form, lang_form=get_lang_form(True),
|
78
|
-
ipv4=hutils.network.get_ip_str(4),
|
79
|
-
ipv6=hutils.network.get_ip_str(6),
|
80
|
-
admin_link=admin_link(),
|
81
|
-
show_domain_info=False)
|
60
|
+
return form.post(self)
|
82
61
|
|
83
62
|
|
84
63
|
def get_lang_form(empty=False):
|
85
64
|
class LangForm(FlaskForm):
|
65
|
+
step = wtf.HiddenField(default="1")
|
86
66
|
admin_lang = wtf.SelectField(
|
87
67
|
_("config.admin_lang.label"), choices=[("en", _("lang.en")), ("fa", _("lang.fa")), ("pt", _("lang.pt")), ("zh", _("lang.zh")), ("ru", _("lang.ru"))],
|
88
68
|
description=_("config.admin_lang.description"),
|
@@ -94,7 +74,61 @@ def get_lang_form(empty=False):
|
|
94
74
|
default=hconfig(ConfigEnum.country))
|
95
75
|
lang_submit = wtf.SubmitField(_('Submit'))
|
96
76
|
|
97
|
-
|
77
|
+
def post(self, view):
|
78
|
+
set_hconfig(ConfigEnum.lang, self.admin_lang.data)
|
79
|
+
set_hconfig(ConfigEnum.admin_lang, self.admin_lang.data)
|
80
|
+
set_hconfig(ConfigEnum.country, self.country.data)
|
81
|
+
|
82
|
+
flask_babel.refresh()
|
83
|
+
hutils.flask.flash((_('quicksetup.setlang.success')), 'success')
|
84
|
+
|
85
|
+
return render_template(
|
86
|
+
'quick_setup.html', form=view.current_form(next=True),
|
87
|
+
admin_link=admin_link(),
|
88
|
+
ipv4=hutils.network.get_ip_str(4),
|
89
|
+
ipv6=hutils.network.get_ip_str(6),
|
90
|
+
show_domain_info=False)
|
91
|
+
|
92
|
+
form = LangForm(None)if empty else LangForm()
|
93
|
+
form.step.data = "1"
|
94
|
+
return form
|
95
|
+
|
96
|
+
|
97
|
+
def get_proxy_form(empty=False):
|
98
|
+
class ProxyForm(FlaskForm):
|
99
|
+
step = wtf.HiddenField(default="3")
|
100
|
+
description_for_fieldset = wtf.TextAreaField("", description=_(f'quicksetup.proxy_cat.description'), render_kw={"class": "d-none"})
|
101
|
+
|
102
|
+
def post(self, view):
|
103
|
+
|
104
|
+
for k, vs in self.data.items():
|
105
|
+
ek = ConfigEnum[k]
|
106
|
+
if ek != ConfigEnum.not_found:
|
107
|
+
set_hconfig(ek, vs, commit=False)
|
108
|
+
|
109
|
+
db.session.commit()
|
110
|
+
# print(cat,vs)
|
111
|
+
hutils.proxy.get_proxies.invalidate_all()
|
112
|
+
if hutils.node.is_child():
|
113
|
+
hutils.node.run_node_op_in_bg(hutils.node.child.sync_with_parent, *[hutils.node.child.SyncFields.hconfigs])
|
114
|
+
|
115
|
+
from .Actions import Actions
|
116
|
+
return Actions().reinstall(domain_changed=True)
|
117
|
+
boolconfigs = BoolConfig.query.filter(BoolConfig.child_id == Child.current().id).all()
|
118
|
+
|
119
|
+
for cf in boolconfigs:
|
120
|
+
if cf.key.category == 'hidden':
|
121
|
+
continue
|
122
|
+
if cf.key.startswith("sub_") or cf.key.startswith("mux_"):
|
123
|
+
continue
|
124
|
+
if not cf.key.endswith("_enable") or cf.key in [ConfigEnum.hysteria_obfs_enable, ConfigEnum.tls_padding_enable]:
|
125
|
+
continue
|
126
|
+
field = SwitchField(_(f'config.{cf.key}.label'), default=cf.value, description=_(f'config.{cf.key}.description'))
|
127
|
+
setattr(ProxyForm, f'{cf.key}', field)
|
128
|
+
setattr(ProxyForm, "submit_global", wtf.fields.SubmitField(_('Submit')))
|
129
|
+
form = ProxyForm(None) if empty else ProxyForm()
|
130
|
+
form.step.data = "3"
|
131
|
+
return form
|
98
132
|
|
99
133
|
|
100
134
|
def get_quick_setup_form(empty=False):
|
@@ -108,14 +142,22 @@ def get_quick_setup_form(empty=False):
|
|
108
142
|
domains.append(d.domain)
|
109
143
|
return domains
|
110
144
|
|
111
|
-
class
|
145
|
+
class BasicConfigs(FlaskForm):
|
146
|
+
step = wtf.HiddenField(default="2")
|
147
|
+
description_for_fieldset = wtf.TextAreaField("", description=_(f'quicksetup.proxy_cat.description'), render_kw={"class": "d-none"})
|
112
148
|
domain_regex = "^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})$"
|
113
149
|
|
114
150
|
domain_validators = [
|
115
|
-
wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.
|
151
|
+
wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.Invalid_domain")),
|
116
152
|
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.
|
153
|
+
wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.Domain_already_used")),
|
154
|
+
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"))]
|
155
|
+
|
156
|
+
cdn_domain_validators = [
|
157
|
+
wtf.validators.Regexp(f'({domain_regex})|(^$)', re.IGNORECASE, _("config.Invalid_domain")),
|
158
|
+
validate_domain_cdn,
|
159
|
+
wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.Domain_already_used")),
|
160
|
+
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
161
|
domain = wtf.StringField(
|
120
162
|
_("domain.domain"),
|
121
163
|
domain_validators,
|
@@ -126,16 +168,50 @@ def get_quick_setup_form(empty=False):
|
|
126
168
|
"title": domain_validators[0].message,
|
127
169
|
"required": "",
|
128
170
|
"placeholder": "sub.domain.com"})
|
171
|
+
|
172
|
+
cdn_domain = wtf.StringField(
|
173
|
+
_("quicksetup.cdn_domain.label"),
|
174
|
+
cdn_domain_validators,
|
175
|
+
description=_("quicksetup.cdn_domain.description"),
|
176
|
+
render_kw={
|
177
|
+
"class": "ltr",
|
178
|
+
"pattern": domain_validators[0].regex.pattern,
|
179
|
+
"title": domain_validators[0].message,
|
180
|
+
"placeholder": "sub.domain.com"})
|
129
181
|
# enable_telegram = SwitchField(_("config.telegram_enable.label"), description=_("config.telegram_enable.description"), default=hconfig(ConfigEnum.telegram_enable))
|
130
182
|
# enable_firewall = SwitchField(_("config.firewall.label"), description=_("config.firewall.description"), default=hconfig(ConfigEnum.firewall))
|
131
183
|
block_iran_sites = SwitchField(_("config.block_iran_sites.label"), description=_(
|
132
184
|
"config.block_iran_sites.description"), default=hconfig(ConfigEnum.block_iran_sites))
|
133
185
|
# enable_vmess = SwitchField(_("config.vmess_enable.label"), description=_("config.vmess_enable.description"), default=hconfig(ConfigEnum.vmess_enable))
|
134
186
|
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.
|
187
|
+
ConfigEnum.decoy_domain), validators=[wtf.validators.Regexp(domain_regex, re.IGNORECASE, _("config.Invalid_domain")), hutils.flask.validate_domain_exist])
|
136
188
|
submit = wtf.SubmitField(_('Submit'))
|
137
189
|
|
138
|
-
|
190
|
+
def post(self, view):
|
191
|
+
Domain.query.filter(Domain.domain == f'{hutils.network.get_ip_str(4)}.sslip.io').delete()
|
192
|
+
db.session.add(Domain(domain=self.domain.data.lower(), mode=DomainType.direct))
|
193
|
+
if self.cdn_domain.data:
|
194
|
+
db.session.add(Domain(domain=self.cdn_domain.data.lower(), mode=DomainType.cdn))
|
195
|
+
set_hconfig(ConfigEnum.block_iran_sites, self.block_iran_sites.data)
|
196
|
+
set_hconfig(ConfigEnum.decoy_domain, self.decoy_domain.data)
|
197
|
+
# hiddify.bulk_register_configs([
|
198
|
+
# # {"key": ConfigEnum.telegram_enable, "value": quick_form.enable_telegram.data == True},
|
199
|
+
# # {"key": ConfigEnum.vmess_enable, "value": quick_form.enable_vmess.data == True},
|
200
|
+
# # {"key": ConfigEnum.firewall, "value": quick_form.enable_firewall.data == True},
|
201
|
+
# {"key": ConfigEnum.block_iran_sites, "value": quick_form.block_iran_sites.data == True},
|
202
|
+
# # {"key":ConfigEnum.decoy_domain,"value":quick_form.decoy_domain.data}
|
203
|
+
# ])
|
204
|
+
|
205
|
+
return render_template(
|
206
|
+
'quick_setup.html', form=view.current_form(next=True),
|
207
|
+
# ipv4=hutils.network.get_ip_str(4),
|
208
|
+
# ipv6=hutils.network.get_ip_str(6),
|
209
|
+
admin_link=admin_link(),
|
210
|
+
show_domain_info=False)
|
211
|
+
|
212
|
+
form = BasicConfigs(None) if empty else BasicConfigs()
|
213
|
+
form.step.data = "2"
|
214
|
+
return form
|
139
215
|
|
140
216
|
|
141
217
|
def validate_domain(form, field):
|
@@ -145,8 +221,24 @@ def validate_domain(form, field):
|
|
145
221
|
raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
|
146
222
|
|
147
223
|
myip = hutils.network.get_ip(4)
|
148
|
-
|
149
|
-
|
224
|
+
myip6 = hutils.network.get_ip(4)
|
225
|
+
if dip and myip != dip and (not myip6 or myip6 != dip):
|
226
|
+
raise ValidationError(_("Domain (%(domain)s)-> IP=%(domain_ip)s is not matched with your ip=%(server_ip)s which is required in direct mode",
|
227
|
+
server_ip=myip, domain_ip=dip, domain=domain))
|
228
|
+
|
229
|
+
|
230
|
+
def validate_domain_cdn(form, field):
|
231
|
+
domain = field.data
|
232
|
+
if not domain:
|
233
|
+
return
|
234
|
+
dip = hutils.network.get_domain_ip(domain)
|
235
|
+
if dip is None:
|
236
|
+
raise ValidationError(_("Domain can not be resolved! there is a problem in your domain"))
|
237
|
+
|
238
|
+
myip = hutils.network.get_ip(4)
|
239
|
+
if myip == dip:
|
240
|
+
raise ValidationError(_("In CDN mode, Domain IP=%(domain_ip)s should be different to your ip=%(server_ip)s",
|
241
|
+
server_ip=myip, domain_ip=dip, domain=domain))
|
150
242
|
|
151
243
|
|
152
244
|
def admin_link():
|
@@ -24,6 +24,7 @@ from hiddifypanel.models import *
|
|
24
24
|
from hiddifypanel.database import db
|
25
25
|
from hiddifypanel.panel import hiddify, custom_widgets
|
26
26
|
from hiddifypanel import __version__
|
27
|
+
from hiddifypanel.cache import cache
|
27
28
|
|
28
29
|
|
29
30
|
class SettingAdmin(FlaskView):
|
@@ -111,6 +112,8 @@ class SettingAdmin(FlaskView):
|
|
111
112
|
if p_mode != PanelMode.standalone:
|
112
113
|
set_hconfig(ConfigEnum.panel_mode, PanelMode.standalone)
|
113
114
|
|
115
|
+
cache.invalidate_all_cached_functions()
|
116
|
+
# hutils.proxy.get_proxies.invalidate_all()
|
114
117
|
from hiddifypanel.panel.commercial.telegrambot import register_bot
|
115
118
|
register_bot(set_hook=True)
|
116
119
|
|
@@ -194,13 +197,15 @@ def get_config_form():
|
|
194
197
|
if c.key in bool_types:
|
195
198
|
field = SwitchField(_(f'config.{c.key}.label'), default=c.value, description=_(f'config.{c.key}.description'))
|
196
199
|
elif c.key == ConfigEnum.core_type:
|
197
|
-
field = wtf.SelectField(_(f"config.{c.key}.label"),
|
198
|
-
|
200
|
+
field = wtf.SelectField(_(f"config.{c.key}.label"),
|
201
|
+
choices=[("xray", _("Xray")), ("singbox", _("SingBox"))],
|
202
|
+
description=_(f"config.{c.key}.description"),
|
203
|
+
default=hconfig(c.key))
|
199
204
|
elif c.key == ConfigEnum.warp_mode:
|
200
|
-
field = wtf.SelectField(
|
201
|
-
|
202
|
-
|
203
|
-
|
205
|
+
field = wtf.SelectField(_(f"config.{c.key}.label"),
|
206
|
+
choices=[("disable", _("Disable")), ("all", _("All")), ("custom", _("Only Blocked and Local websites"))],
|
207
|
+
description=_(f"config.{c.key}.description"),
|
208
|
+
default=hconfig(c.key))
|
204
209
|
|
205
210
|
elif c.key == ConfigEnum.lang or c.key == ConfigEnum.admin_lang:
|
206
211
|
field = wtf.SelectField(
|
@@ -267,16 +272,16 @@ def get_config_form():
|
|
267
272
|
render_kw = {'class': "ltr"}
|
268
273
|
validators = []
|
269
274
|
if c.key == ConfigEnum.domain_fronting_domain:
|
270
|
-
validators.append(wtf.validators.Regexp("^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})|$", re.IGNORECASE, _("config.
|
275
|
+
validators.append(wtf.validators.Regexp("^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})|$", re.IGNORECASE, _("config.Invalid_domain")))
|
271
276
|
validators.append(hutils.flask.validate_domain_exist)
|
272
277
|
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.
|
278
|
+
validators.append(wtf.validators.Regexp("^([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})$", re.IGNORECASE, _("config.Invalid_domain")))
|
274
279
|
validators.append(hutils.flask.validate_domain_exist)
|
275
280
|
|
276
281
|
if c.key != ConfigEnum.decoy_domain:
|
277
|
-
validators.append(wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.
|
282
|
+
validators.append(wtf.validators.NoneOf([d.domain.lower() for d in Domain.query.all()], _("config.Domain_already_used")))
|
278
283
|
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.
|
284
|
+
[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
285
|
|
281
286
|
render_kw['required'] = ""
|
282
287
|
if len(c.value) < 3:
|
@@ -292,36 +297,36 @@ def get_config_form():
|
|
292
297
|
if c.key == ConfigEnum.parent_panel:
|
293
298
|
validators.append(wtf.validators.Regexp("()|(http(s|)://([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})/.*)", re.IGNORECASE, _("Invalid admin link")))
|
294
299
|
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.
|
300
|
+
validators.append(wtf.validators.Regexp("()|^([0-9]{8,12}:[a-zA-Z0-9_-]{30,40})|$", re.IGNORECASE, _("config.Invalid_telegram_bot_token")))
|
296
301
|
if c.key == ConfigEnum.branding_site:
|
297
302
|
validators.append(wtf.validators.Regexp(
|
298
|
-
"()|(http(s|)://([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})/?.*)", re.IGNORECASE, _("config.
|
303
|
+
"()|(http(s|)://([A-Za-z0-9\\-\\.]+\\.[a-zA-Z]{2,})/?.*)", re.IGNORECASE, _("config.Invalid_brand_link")))
|
299
304
|
# render_kw['required']=""
|
300
305
|
|
301
306
|
if 'secret' in c.key:
|
302
307
|
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.
|
308
|
+
"^[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
309
|
render_kw['required'] = ""
|
305
310
|
|
306
311
|
if c.key == ConfigEnum.proxy_path:
|
307
|
-
validators.append(wtf.validators.Regexp("^[a-zA-Z0-9]*$", re.IGNORECASE, _("config.
|
312
|
+
validators.append(wtf.validators.Regexp("^[a-zA-Z0-9]*$", re.IGNORECASE, _("config.Invalid_proxy_path")))
|
308
313
|
render_kw['required'] = ""
|
309
314
|
|
310
315
|
if 'port' in c.key:
|
311
316
|
if c.key in [ConfigEnum.http_ports, ConfigEnum.tls_ports]:
|
312
|
-
validators.append(wtf.validators.Regexp("^(\\d+)(,\\d+)*$", re.IGNORECASE, _("config.
|
317
|
+
validators.append(wtf.validators.Regexp("^(\\d+)(,\\d+)*$", re.IGNORECASE, _("config.Invalid_port")))
|
313
318
|
render_kw['required'] = ""
|
314
319
|
else:
|
315
|
-
validators.append(wtf.validators.Regexp("^(\\d+)(,\\d+)*$|^$", re.IGNORECASE, _("config.
|
320
|
+
validators.append(wtf.validators.Regexp("^(\\d+)(,\\d+)*$|^$", re.IGNORECASE, _("config.Invalid_port")))
|
316
321
|
# validators.append(wtf.validators.Regexp("^(\d+)(,\d+)*$",re.IGNORECASE,_("config.port is required")))
|
317
322
|
|
318
323
|
# tls tricks validations
|
319
324
|
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.
|
325
|
+
validators.append(wtf.validators.Regexp("^\\d+-\\d+$", re.IGNORECASE, _("config.Invalid_The_pattern_is_number-number") + f' {c.key}'))
|
321
326
|
# mux and hysteria validations
|
322
327
|
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
328
|
ConfigEnum.mux_brutal_down_mbps, ConfigEnum.mux_brutal_up_mbps]:
|
324
|
-
validators.append(wtf.validators.Regexp("^\\d+$", re.IGNORECASE, _("config.
|
329
|
+
validators.append(wtf.validators.Regexp("^\\d+$", re.IGNORECASE, _("config.Invalid_it_should_be_a_number_only") + f' {c.key}'))
|
325
330
|
|
326
331
|
for val in validators:
|
327
332
|
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
|
@@ -43,26 +43,17 @@
|
|
43
43
|
|
44
44
|
|
45
45
|
<div class="row">
|
46
|
-
<div class="card col-12">
|
47
|
-
<div class="card-header">{{_("config.lang.label")}}</div>
|
48
|
-
<div class="card-body">
|
49
46
|
|
50
|
-
<div class="row">
|
51
|
-
<div class="col-12">
|
52
47
|
|
53
|
-
|
54
|
-
</div>
|
55
|
-
</div>
|
56
|
-
</div>
|
57
|
-
</div>
|
58
|
-
|
59
|
-
<div class="card">
|
48
|
+
<div class="card col-12">
|
60
49
|
<div class="card-header">{{_("admin.quicksetup.title")}}</div>
|
61
50
|
<div class="card-body">
|
62
51
|
|
63
52
|
<div class="row">
|
64
53
|
<div class="card-columns">
|
54
|
+
{% if ipv4 or ipv6 %}
|
65
55
|
{{_('admin.quicksetup_intro',ipv4=ip_btn(ipv4),ipv6=ip_btn(ipv6))}}
|
56
|
+
{%endif%}
|
66
57
|
{{render_form(form,form_type="",extra_classes="card-columns1")}}
|
67
58
|
</div>
|
68
59
|
</div>
|
@@ -71,9 +62,24 @@
|
|
71
62
|
</div>
|
72
63
|
|
73
64
|
<style>
|
65
|
+
.form-group {
|
66
|
+
display: inline-block;
|
67
|
+
width: 100%;
|
68
|
+
}
|
69
|
+
|
70
|
+
fieldset {
|
71
|
+
margin: 20px 0;
|
72
|
+
}
|
73
|
+
|
74
|
+
legend {
|
75
|
+
background: transparent;
|
76
|
+
margin-bottom: 20px;
|
77
|
+
|
78
|
+
}
|
79
|
+
|
74
80
|
@media (min-width: 576px) {
|
75
81
|
.card-columns {
|
76
|
-
column-count:
|
82
|
+
column-count: 2;
|
77
83
|
}
|
78
84
|
}
|
79
85
|
|
@@ -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)
|