hiddifypanel 10.50.5.dev0__py3-none-any.whl → 10.60.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/hutils/flask.py +5 -3
  4. hiddifypanel/hutils/network/net.py +22 -11
  5. hiddifypanel/hutils/proxy/singbox.py +13 -0
  6. hiddifypanel/hutils/proxy/xray.py +3 -0
  7. hiddifypanel/models/config_enum.py +114 -114
  8. hiddifypanel/panel/admin/Actions.py +1 -1
  9. hiddifypanel/panel/admin/DomainAdmin.py +2 -2
  10. hiddifypanel/panel/admin/ProxyAdmin.py +1 -1
  11. hiddifypanel/panel/admin/SettingAdmin.py +2 -2
  12. hiddifypanel/panel/admin/templates/config.html +129 -1
  13. hiddifypanel/panel/commercial/restapi/v1/__init__.py +1 -1
  14. hiddifypanel/panel/commercial/restapi/v1/tgbot.py +6 -0
  15. hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +2 -2
  16. hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +9 -1
  17. hiddifypanel/panel/commercial/telegrambot/__init__.py +1 -1
  18. hiddifypanel/panel/common.py +3 -1
  19. hiddifypanel/panel/custom_widgets.py +3 -0
  20. hiddifypanel/panel/hiddify.py +7 -4
  21. hiddifypanel/panel/init_db.py +4 -0
  22. hiddifypanel/panel/usage.py +2 -2
  23. hiddifypanel/panel/user/user.py +38 -0
  24. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  25. hiddifypanel/translations/en/LC_MESSAGES/messages.po +9 -2
  26. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  27. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +7 -2
  28. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  29. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +7 -2
  30. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  31. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +7 -2
  32. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  33. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +7 -2
  34. hiddifypanel/translations.i18n/en.json +2 -1
  35. hiddifypanel/translations.i18n/fa.json +2 -1
  36. hiddifypanel/translations.i18n/pt.json +2 -1
  37. hiddifypanel/translations.i18n/ru.json +2 -1
  38. hiddifypanel/translations.i18n/zh.json +2 -1
  39. {hiddifypanel-10.50.5.dev0.dist-info → hiddifypanel-10.60.0.dev0.dist-info}/METADATA +1 -1
  40. {hiddifypanel-10.50.5.dev0.dist-info → hiddifypanel-10.60.0.dev0.dist-info}/RECORD +44 -44
  41. {hiddifypanel-10.50.5.dev0.dist-info → hiddifypanel-10.60.0.dev0.dist-info}/WHEEL +1 -1
  42. {hiddifypanel-10.50.5.dev0.dist-info → hiddifypanel-10.60.0.dev0.dist-info}/LICENSE.md +0 -0
  43. {hiddifypanel-10.50.5.dev0.dist-info → hiddifypanel-10.60.0.dev0.dist-info}/entry_points.txt +0 -0
  44. {hiddifypanel-10.50.5.dev0.dist-info → hiddifypanel-10.60.0.dev0.dist-info}/top_level.txt +0 -0
@@ -26,7 +26,8 @@
26
26
 
27
27
 
28
28
  <div class="card">
29
- <div class="card-header"><input id="search_settings" type="text" class="form-control" placeholder="{{_(" Search Settings")}}">
29
+ <div class="card-header"><input id="search_settings" type="text" class="form-control" placeholder="{{_(" Search
30
+ Settings")}}">
30
31
  </div>
31
32
  <div class="card-body">
32
33
  <div class="row">
@@ -81,6 +82,133 @@
81
82
 
82
83
  {% block tail %}
83
84
  {{super()}}
85
+ <link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/42.0.2/ckeditor5.css">
86
+ <style>
87
+ .ck.ck-editor {
88
+ color: black !important;
89
+ }
90
+ </style>
91
+ <script src="https://cdn.ckeditor.com/ckeditor5/42.0.2/ckeditor5.js"></script>
92
+
93
+ <script type="importmap">
94
+ {
95
+ "imports": {
96
+ "ckeditor5": "https://cdn.ckeditor.com/ckeditor5/42.0.2/ckeditor5.js",
97
+ "ckeditor5/": "https://cdn.ckeditor.com/ckeditor5/42.0.2/"
98
+ }
99
+ }
100
+ </script>
101
+ <script type="module">
102
+ import {
103
+ ClassicEditor,
104
+ AccessibilityHelp,
105
+ Autosave,
106
+ BalloonToolbar,
107
+ Bold,
108
+ Code,
109
+ Essentials,
110
+ FontBackgroundColor,
111
+ FontColor,
112
+ FontFamily,
113
+ FontSize,
114
+ FullPage,
115
+ GeneralHtmlSupport,
116
+ Highlight,
117
+ HtmlComment,
118
+ HtmlEmbed,
119
+ Italic,
120
+ Paragraph,
121
+ SelectAll,
122
+ ShowBlocks,
123
+ SourceEditing,
124
+ SpecialCharacters,
125
+ Strikethrough,
126
+ ImageBlock,
127
+ ImageInsert,
128
+ ImageInsertViaUrl,
129
+ ImageToolbar,
130
+
131
+ Underline,
132
+ Undo
133
+ } from 'ckeditor5';
134
+
135
+ const editorConfig = {
136
+ toolbar: {
137
+ items: [
138
+ 'undo',
139
+ 'redo',
140
+ '|',
141
+ 'bold',
142
+ 'italic',
143
+ 'underline',
144
+ 'strikethrough',
145
+ 'code',
146
+ 'insertImage',
147
+ '|',
148
+ 'fontSize',
149
+ 'fontColor',
150
+ 'fontBackgroundColor',
151
+ '|',
152
+ 'sourceEditing',
153
+
154
+ ],
155
+ shouldNotGroupWhenFull: false
156
+ },
157
+ plugins: [
158
+ AccessibilityHelp,
159
+ Autosave,
160
+ BalloonToolbar,
161
+ Bold,
162
+ Code,
163
+ Essentials,
164
+ FontBackgroundColor,
165
+ FontColor,
166
+ FontFamily,
167
+ FontSize,
168
+ FullPage,
169
+ GeneralHtmlSupport,
170
+ Highlight,
171
+ HtmlComment,
172
+ HtmlEmbed,
173
+ Italic,
174
+ Paragraph,
175
+ SelectAll,
176
+ ShowBlocks,
177
+ SourceEditing,
178
+ ImageBlock,
179
+ ImageInsertViaUrl,
180
+
181
+ Strikethrough,
182
+ Underline,
183
+ Undo
184
+ ],
185
+ balloonToolbar: ['bold', 'italic'],
186
+ fontFamily: {
187
+ supportAllValues: true
188
+ },
189
+ fontSize: {
190
+ options: [10, 12, 14, 'default', 18, 20, 22],
191
+ supportAllValues: true
192
+ },
193
+ htmlSupport: {
194
+ allow: [
195
+ {
196
+ name: /^.*$/,
197
+ styles: true,
198
+ attributes: true,
199
+ classes: true
200
+ }
201
+ ]
202
+ },
203
+
204
+ placeholder: 'Type or paste your content here!'
205
+ };
206
+
207
+ ClassicEditor.create(document.querySelector('.ckeditor'), editorConfig);
208
+
209
+
210
+ </script>
211
+
84
212
  <script>
85
213
  // $("fieldset .form-group").hide()
86
214
 
@@ -5,7 +5,7 @@ from marshmallow import Schema, fields
5
5
 
6
6
  from apiflask import APIBlueprint
7
7
  from flask_restful import Api
8
- from .tgbot import bot, register_bot, TGBotResource
8
+ from .tgbot import bot, register_bot, register_bot_cached, TGBotResource
9
9
  from . import tgbot
10
10
  from .tgmsg import SendMsgResource
11
11
  from .resources import *
@@ -5,6 +5,7 @@ from flask_restful import Resource
5
5
 
6
6
  from hiddifypanel.models import *
7
7
  from hiddifypanel import Events
8
+ from hiddifypanel.cache import cache
8
9
  logger = telebot.logger
9
10
 
10
11
 
@@ -17,6 +18,11 @@ bot = telebot.TeleBot("", parse_mode="HTML", threaded=False, exception_handler=E
17
18
  bot.username = ''
18
19
 
19
20
 
21
+ @cache.cache(1000)
22
+ def register_bot_cached(set_hook=False, remove_hook=False):
23
+ return register_bot(set_hook, remove_hook)
24
+
25
+
20
26
  def register_bot(set_hook=False, remove_hook=False):
21
27
  try:
22
28
  global bot
@@ -35,8 +35,8 @@ class UserApi(MethodView):
35
35
  for field in User.__table__.columns.keys(): # type: ignore
36
36
  if field in ['id', 'expiry_time']:
37
37
  continue
38
- if field not in data:
39
- data[field] = getattr(user, field)
38
+ # if field in data:
39
+ # setattr(user, field, data[field])
40
40
  data['old_uuid'] = uuid
41
41
  user_driver.remove_client(user)
42
42
  dbuser = User.add_or_update(**data) or abort(502, "Unknown issue! User is not patched")
@@ -108,7 +108,15 @@ class AllConfigsAPI(MethodView):
108
108
  )
109
109
  )
110
110
 
111
- # Add Singbox: SSh
111
+ if hconfig(ConfigEnum.wireguard_enable):
112
+ items.append(
113
+ create_item(
114
+ "Wireguard", "Wireguard", "", "", "", "",
115
+ # f"{base_url}singbox.json?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
116
+ f"{base_url}wireguard/#{config_name}"
117
+ )
118
+ )
119
+ # Add Singbox: SSh
112
120
  if hconfig(ConfigEnum.sub_singbox_ssh_enable) and hconfig(ConfigEnum.ssh_server_enable):
113
121
  items.append(
114
122
  create_item(
@@ -1,4 +1,4 @@
1
- from hiddifypanel.panel.commercial.restapi.v1 import bot, register_bot
1
+ from hiddifypanel.panel.commercial.restapi.v1 import bot, register_bot, register_bot_cached
2
2
 
3
3
  from . import Usage
4
4
  from . import information
@@ -10,6 +10,7 @@ import hiddifypanel.auth as auth
10
10
  from hiddifypanel.auth import current_account
11
11
  from apiflask import APIFlask, HTTPError, abort
12
12
  from hiddifypanel import hutils
13
+ from loguru import logger
13
14
 
14
15
 
15
16
  def init_app(app: APIFlask):
@@ -36,6 +37,7 @@ def init_app(app: APIFlask):
36
37
 
37
38
  @app.errorhandler(Exception)
38
39
  def internal_server_error(e):
40
+ logger.exception(e)
39
41
  if isinstance(e, Exception):
40
42
  if hutils.flask.is_api_call(request.path):
41
43
  return {
@@ -181,4 +183,4 @@ def init_app(app: APIFlask):
181
183
  with app.app_context():
182
184
  import hiddifypanel.panel.commercial.telegrambot as telegrambot
183
185
  if (not telegrambot.bot) or (not telegrambot.bot.username): # type: ignore
184
- telegrambot.register_bot(set_hook=True)
186
+ telegrambot.register_bot_cached(set_hook=True)
@@ -47,6 +47,8 @@ class LastResetField(IntegerField):
47
47
 
48
48
 
49
49
  class CKTextAreaWidget(TextArea):
50
+ extra_js = ['//cdn.ckeditor.com/4.6.0/standard/ckeditor.js']
51
+
50
52
  def __call__(self, field, **kwargs):
51
53
  if kwargs.get('class'):
52
54
  kwargs['class'] += ' ckeditor'
@@ -56,6 +58,7 @@ class CKTextAreaWidget(TextArea):
56
58
 
57
59
 
58
60
  class CKTextAreaField(TextAreaField):
61
+ extra_js = ['//cdn.ckeditor.com/4.6.0/standard/ckeditor.js']
59
62
  widget = CKTextAreaWidget()
60
63
 
61
64
 
@@ -110,17 +110,20 @@ def reinstall_action(complete_install=False, domain_changed=False, do_update=Fal
110
110
  def check_need_reset(old_configs, do=False):
111
111
  restart_mode = ApplyMode.nothing
112
112
  for c in old_configs:
113
+ if c.apply_mode == ApplyMode.nothing:
114
+ continue
113
115
  # c=ConfigEnum(c)
114
- if old_configs[c] != hconfig(c) and c.apply_mode != ApplyMode.nothing:
115
- if restart_mode != ApplyMode.restart:
116
- restart_mode = c.apply_mode
116
+ if restart_mode == ApplyMode.reinstall:
117
+ break
118
+ if old_configs[c] != hconfig(c):
119
+ restart_mode = c.apply_mode
117
120
  if old_configs[ConfigEnum.proxy_path_admin] != hconfig(ConfigEnum.proxy_path_admin):
118
121
  g.new_proxy_path = hconfig(ConfigEnum.proxy_path_admin)
119
122
  g.force_proxy_path = g.proxy_path
120
123
  # do_full_install=old_config[ConfigEnum.telegram_lib]!=hconfig(ConfigEnum.telegram_lib)
121
124
  if old_configs[ConfigEnum.package_mode] != hconfig(ConfigEnum.package_mode):
122
125
  return reinstall_action(do_update=True)
123
- if not (do and restart_mode == ApplyMode.restart):
126
+ if not (do and restart_mode == ApplyMode.reinstall):
124
127
  return hutils.flask.flash_config_success(restart_mode=restart_mode, domain_changed=False)
125
128
 
126
129
  return reinstall_action(complete_install=True, domain_changed=False)
@@ -17,6 +17,10 @@ from loguru import logger
17
17
  MAX_DB_VERSION = 100
18
18
 
19
19
 
20
+ def _v91(child_id):
21
+ db.session.bulk_save_objects(get_proxy_rows_v1())
22
+
23
+
20
24
  def _v90(child_id):
21
25
  result = (
22
26
  db.session.query(
@@ -71,8 +71,8 @@ def _add_users_usage(users_usage_data: Dict[User, Dict], child_id, sync=False):
71
71
  db.session.add(daily_usage[adm.id])
72
72
  changes = True
73
73
  daily_usage[adm.id].online = User.query.filter(User.added_by == adm.id).filter(func.DATE(User.last_online) == today).count()
74
- if changes:
75
- db.session.commit()
74
+ # if changes:
75
+ # db.session.commit()
76
76
  _reset_priodic_usage()
77
77
 
78
78
  # userDetails = {p.user_id: p for p in UserDetail.query.filter(UserDetail.child_id == child_id).all()}
@@ -73,6 +73,44 @@ class UserView(FlaskView):
73
73
  '''Returns singbox client JSON config (ssh)'''
74
74
  return self.singbox_ssh_imp()
75
75
 
76
+ @route("/wireguard/")
77
+ @route("/wireguard")
78
+ @login_required(roles={Role.user})
79
+ def wireguard(self):
80
+ '''Returns wireguard client config'''
81
+ c = get_common_data(g.account.uuid, 'new')
82
+ wireguards = []
83
+ servers = set()
84
+ for pinfo in hutils.proxy.get_valid_proxies(c['domains']):
85
+ if pinfo['proto'] != ProxyProto.wireguard:
86
+ continue
87
+ wireguards.append(pinfo)
88
+
89
+ servers.add(pinfo['server'])
90
+
91
+ if not len(wireguards):
92
+ abort(404)
93
+ wg = wireguards[0]
94
+ addrs = f"{wg['wg_ipv4']}/32"
95
+ if wg['wg_ipv6']:
96
+ addrs += f", {wg['wg_ipv6']}/128"
97
+ resp = f"""
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
+ """
110
+ return add_headers(resp, c)
111
+
112
+ # return self.singbox_ssh_imp()
113
+
76
114
  @route("/clash/")
77
115
  @route("/clash")
78
116
  @login_required(roles={Role.user})
@@ -2,8 +2,12 @@
2
2
  msgid ""
3
3
  msgstr ""
4
4
 
5
- msgid " Search Settings"
6
- msgstr " Search Settings"
5
+ msgid ""
6
+ " Search\n"
7
+ " Settings"
8
+ msgstr ""
9
+ " Search\n"
10
+ " Settings"
7
11
 
8
12
  msgid "%(count)s records were successfully disabled."
9
13
  msgstr "%(count)s records were successfully disabled."
@@ -670,6 +674,9 @@ msgstr "Select this option to restore All Users"
670
674
  msgid "Root"
671
675
  msgstr "Root"
672
676
 
677
+ msgid "Russia"
678
+ msgstr "Russia"
679
+
673
680
  msgid "Save"
674
681
  msgstr "Save"
675
682
 
@@ -2,8 +2,10 @@
2
2
  msgid ""
3
3
  msgstr ""
4
4
 
5
- msgid " Search Settings"
6
- msgstr " تنظیمات جستجو"
5
+ msgid ""
6
+ " Search\n"
7
+ " Settings"
8
+ msgstr ""
7
9
 
8
10
  msgid "%(count)s records were successfully disabled."
9
11
  msgstr "%(count)s رکورد با موفقیت غیرفعال شد."
@@ -654,6 +656,9 @@ msgstr "برای بازگرداندن کاربران از این گزینه اس
654
656
  msgid "Root"
655
657
  msgstr "ریشه"
656
658
 
659
+ msgid "Russia"
660
+ msgstr ""
661
+
657
662
  msgid "Save"
658
663
  msgstr "ذخیره"
659
664
 
@@ -2,8 +2,10 @@
2
2
  msgid ""
3
3
  msgstr ""
4
4
 
5
- msgid " Search Settings"
6
- msgstr " Configurações de pesquisa"
5
+ msgid ""
6
+ " Search\n"
7
+ " Settings"
8
+ msgstr ""
7
9
 
8
10
  msgid "%(count)s records were successfully disabled."
9
11
  msgstr "%(count)s registros foram desativados com sucesso."
@@ -609,6 +611,9 @@ msgstr "Selecione esta opção para restaurar todos os usuários"
609
611
  msgid "Root"
610
612
  msgstr "Raíz"
611
613
 
614
+ msgid "Russia"
615
+ msgstr ""
616
+
612
617
  msgid "Save"
613
618
  msgstr "Salvar"
614
619
 
@@ -2,8 +2,10 @@
2
2
  msgid ""
3
3
  msgstr ""
4
4
 
5
- msgid " Search Settings"
6
- msgstr " Настройки поиска"
5
+ msgid ""
6
+ " Search\n"
7
+ " Settings"
8
+ msgstr ""
7
9
 
8
10
  msgid "%(count)s records were successfully disabled."
9
11
  msgstr "%(count)s записей были успешно отключены."
@@ -668,6 +670,9 @@ msgstr "Выберите этот вариант, чтобы восстанов
668
670
  msgid "Root"
669
671
  msgstr "Root"
670
672
 
673
+ msgid "Russia"
674
+ msgstr ""
675
+
671
676
  msgid "Save"
672
677
  msgstr "Сохранить"
673
678
 
@@ -2,8 +2,10 @@
2
2
  msgid ""
3
3
  msgstr ""
4
4
 
5
- msgid " Search Settings"
6
- msgstr "搜索设置"
5
+ msgid ""
6
+ " Search\n"
7
+ " Settings"
8
+ msgstr ""
7
9
 
8
10
  msgid "%(count)s records were successfully disabled."
9
11
  msgstr "%(count)s 条记录已成功禁用。"
@@ -647,6 +649,9 @@ msgstr "选择此选项可恢复所有用户"
647
649
  msgid "Root"
648
650
  msgstr "根用户"
649
651
 
652
+ msgid "Russia"
653
+ msgstr ""
654
+
650
655
  msgid "Save"
651
656
  msgstr "保存"
652
657
 
@@ -1,5 +1,5 @@
1
1
  {
2
- " Search Settings": " Search Settings",
2
+ " Search\n Settings": " Search\n Settings",
3
3
  "%(count)s records were successfully disabled.": "%(count)s records were successfully disabled.",
4
4
  "%(count)s records were successfully enabled.": "%(count)s records were successfully enabled.",
5
5
  "%(expire_days)s days": "%(expire_days)s days",
@@ -172,6 +172,7 @@
172
172
  "Restore Users": "👥 Restore Users",
173
173
  "Restore Users description": "Select this option to restore All Users",
174
174
  "Root": "Root",
175
+ "Russia": "Russia",
175
176
  "Save": "Save",
176
177
  "Save & Add More": "Save & Add More",
177
178
  "Save Link": "Save Link",
@@ -1,5 +1,5 @@
1
1
  {
2
- " Search Settings": " تنظیمات جستجو",
2
+ " Search\n Settings": "",
3
3
  "%(count)s records were successfully disabled.": "%(count)s رکورد با موفقیت غیرفعال شد.",
4
4
  "%(count)s records were successfully enabled.": "%(count)s رکوردها با موفقیت فعال شد.",
5
5
  "%(expire_days)s days": "%(expire_days)s روز",
@@ -172,6 +172,7 @@
172
172
  "Restore Users": "👥 بازیابی کاربران",
173
173
  "Restore Users description": "برای بازگرداندن کاربران از این گزینه استفاده کنید.",
174
174
  "Root": "ریشه",
175
+ "Russia": "",
175
176
  "Save": "ذخیره",
176
177
  "Save & Add More": "ذخیره و افزودن مورد دیگر",
177
178
  "Save Link": "این لینک را در محلی ذخیره نمایید.",
@@ -1,5 +1,5 @@
1
1
  {
2
- " Search Settings": " Configurações de pesquisa",
2
+ " Search\n Settings": "",
3
3
  "%(count)s records were successfully disabled.": "%(count)s registros foram desativados com sucesso.",
4
4
  "%(count)s records were successfully enabled.": "%(count)s registros foram ativados com sucesso.",
5
5
  "%(expire_days)s days": "",
@@ -172,6 +172,7 @@
172
172
  "Restore Users": "👥 Restaurar usuários",
173
173
  "Restore Users description": "Selecione esta opção para restaurar todos os usuários",
174
174
  "Root": "Raíz",
175
+ "Russia": "",
175
176
  "Save": "Salvar",
176
177
  "Save & Add More": "Salvar e adicionar mais",
177
178
  "Save Link": "Link salvo",
@@ -1,5 +1,5 @@
1
1
  {
2
- " Search Settings": " Настройки поиска",
2
+ " Search\n Settings": "",
3
3
  "%(count)s records were successfully disabled.": "%(count)s записей были успешно отключены.",
4
4
  "%(count)s records were successfully enabled.": "%(count)s записей были успешно включены.",
5
5
  "%(expire_days)s days": "с дней",
@@ -172,6 +172,7 @@
172
172
  "Restore Users": "👥 Восстановление пользователей",
173
173
  "Restore Users description": "Выберите этот вариант, чтобы восстановить всех пользователей",
174
174
  "Root": "Root",
175
+ "Russia": "",
175
176
  "Save": "Сохранить",
176
177
  "Save & Add More": "Сохранить и добавить еще",
177
178
  "Save Link": "Сохранить ссылку",
@@ -1,5 +1,5 @@
1
1
  {
2
- " Search Settings": "搜索设置",
2
+ " Search\n Settings": "",
3
3
  "%(count)s records were successfully disabled.": "%(count)s 条记录已成功禁用。",
4
4
  "%(count)s records were successfully enabled.": "%(count)s 条记录已成功启用。",
5
5
  "%(expire_days)s days": "",
@@ -172,6 +172,7 @@
172
172
  "Restore Users": "👥 恢复用户",
173
173
  "Restore Users description": "选择此选项可恢复所有用户",
174
174
  "Root": "根用户",
175
+ "Russia": "",
175
176
  "Save": "保存",
176
177
  "Save & Add More": "保存并添加更多",
177
178
  "Save Link": "保存链接",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hiddifypanel
3
- Version: 10.50.5.dev0
3
+ Version: 10.60.0.dev0
4
4
  Summary: hiddifypanel multi proxy panel
5
5
  Home-page: https://github.com/hiddify/hiddify-manager/
6
6
  Author: hiddify