hiddifypanel 10.70.8__py3-none-any.whl → 10.80.0__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 (89) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +5 -2
  3. hiddifypanel/__init__.py +5 -1
  4. hiddifypanel/apps/__init__.py +0 -0
  5. hiddifypanel/apps/asgi_app.py +7 -0
  6. hiddifypanel/apps/celery_app.py +3 -0
  7. hiddifypanel/apps/wsgi_app.py +5 -0
  8. hiddifypanel/auth.py +8 -3
  9. hiddifypanel/base.py +43 -126
  10. hiddifypanel/base_setup.py +82 -0
  11. hiddifypanel/cache.py +3 -2
  12. hiddifypanel/celery.py +45 -0
  13. hiddifypanel/database.py +7 -0
  14. hiddifypanel/drivers/ssh_liberty_bridge_api.py +2 -1
  15. hiddifypanel/drivers/wireguard_api.py +1 -1
  16. hiddifypanel/hutils/crypto.py +27 -0
  17. hiddifypanel/hutils/flask.py +5 -3
  18. hiddifypanel/hutils/network/cf_api.py +5 -5
  19. hiddifypanel/hutils/proxy/__init__.py +1 -0
  20. hiddifypanel/hutils/proxy/clash.py +3 -3
  21. hiddifypanel/hutils/proxy/shared.py +14 -18
  22. hiddifypanel/hutils/proxy/singbox.py +4 -2
  23. hiddifypanel/hutils/proxy/wireguard.py +34 -0
  24. hiddifypanel/hutils/proxy/xray.py +3 -3
  25. hiddifypanel/hutils/proxy/xrayjson.py +10 -7
  26. hiddifypanel/models/admin.py +1 -1
  27. hiddifypanel/models/base_account.py +3 -0
  28. hiddifypanel/models/config.py +5 -2
  29. hiddifypanel/models/config_enum.py +15 -2
  30. hiddifypanel/models/proxy.py +1 -1
  31. hiddifypanel/models/user.py +2 -2
  32. hiddifypanel/panel/__init__.py +8 -8
  33. hiddifypanel/panel/admin/AdminstratorAdmin.py +16 -10
  34. hiddifypanel/panel/admin/DomainAdmin.py +132 -98
  35. hiddifypanel/panel/admin/ProxyAdmin.py +4 -0
  36. hiddifypanel/panel/admin/QuickSetup.py +48 -17
  37. hiddifypanel/panel/admin/SettingAdmin.py +6 -0
  38. hiddifypanel/panel/admin/UserAdmin.py +63 -36
  39. hiddifypanel/panel/admin/adminlte.py +1 -1
  40. hiddifypanel/panel/admin/templates/index.html +6 -4
  41. hiddifypanel/panel/admin/templates/model/user_list.html +11 -3
  42. hiddifypanel/panel/cli.py +14 -3
  43. hiddifypanel/panel/commercial/restapi/v1/tgbot.py +19 -1
  44. hiddifypanel/panel/commercial/restapi/v2/admin/system_actions.py +5 -1
  45. hiddifypanel/panel/commercial/restapi/v2/admin/user_api.py +2 -1
  46. hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +76 -6
  47. hiddifypanel/panel/common.py +5 -2
  48. hiddifypanel/panel/common_bp/login.py +14 -8
  49. hiddifypanel/panel/hlogger.py +32 -0
  50. hiddifypanel/panel/init_db.py +157 -77
  51. hiddifypanel/panel/node/__init__.py +9 -0
  52. hiddifypanel/panel/node/a.py +14 -0
  53. hiddifypanel/panel/node/hello.py +14 -0
  54. hiddifypanel/panel/node/test.proto +13 -0
  55. hiddifypanel/panel/node/test_grpc.py +40 -0
  56. hiddifypanel/panel/node/test_pb2.py +40 -0
  57. hiddifypanel/panel/node/test_pb2.pyi +17 -0
  58. hiddifypanel/panel/node/test_pb2_grpc.py +97 -0
  59. hiddifypanel/panel/usage.py +13 -3
  60. hiddifypanel/panel/user/templates/base_singbox_config.json.j2 +16 -0
  61. hiddifypanel/panel/user/templates/home/home.html +1 -2
  62. hiddifypanel/panel/user/templates/home/multi.html +1 -2
  63. hiddifypanel/panel/user/user.py +13 -19
  64. hiddifypanel/static/apps-icon/singbox.ico +0 -0
  65. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  66. hiddifypanel/translations/en/LC_MESSAGES/messages.po +125 -30
  67. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  68. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +123 -32
  69. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  70. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +114 -22
  71. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  72. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +129 -32
  73. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  74. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +107 -18
  75. hiddifypanel/translations.i18n/en.json +73 -14
  76. hiddifypanel/translations.i18n/fa.json +72 -13
  77. hiddifypanel/translations.i18n/fr.json +28 -10
  78. hiddifypanel/translations.i18n/my.json +1266 -0
  79. hiddifypanel/translations.i18n/pt.json +68 -9
  80. hiddifypanel/translations.i18n/ru.json +75 -16
  81. hiddifypanel/translations.i18n/zh.json +67 -8
  82. hiddifypanel-10.80.0.dist-info/METADATA +137 -0
  83. {hiddifypanel-10.70.8.dist-info → hiddifypanel-10.80.0.dist-info}/RECORD +136 -119
  84. {hiddifypanel-10.70.8.dist-info → hiddifypanel-10.80.0.dist-info}/WHEEL +1 -2
  85. hiddifypanel-10.80.0.dist-info/entry_points.txt +3 -0
  86. hiddifypanel-10.70.8.dist-info/METADATA +0 -144
  87. hiddifypanel-10.70.8.dist-info/entry_points.txt +0 -2
  88. hiddifypanel-10.70.8.dist-info/top_level.txt +0 -1
  89. {hiddifypanel-10.70.8.dist-info → hiddifypanel-10.80.0.dist-info}/LICENSE.md +0 -0
@@ -192,10 +192,12 @@
192
192
  }
193
193
 
194
194
  function update_from_json(data) {
195
- usage_history = data['usage_history']
196
- onlines = usage_history['m5']['online']
197
- total_users = usage_history['total']['users']
198
- stats = data['stats']
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');
hiddifypanel/panel/cli.py CHANGED
@@ -13,6 +13,7 @@ from hiddifypanel.panel import hiddify, usage
13
13
  from hiddifypanel.database import db
14
14
  from hiddifypanel.panel.init_db import init_db
15
15
 
16
+ from loguru import logger
16
17
 
17
18
  def drop_db():
18
19
  """Cleans database"""
@@ -29,7 +30,12 @@ def downgrade():
29
30
  os.rename("/opt/hiddify-manager/hiddify-panel/hiddifypanel.db.old", "/opt/hiddify-manager/hiddify-panel/hiddifypanel.db")
30
31
 
31
32
 
33
+ from celery import shared_task
32
34
  def backup():
35
+ backup_task()
36
+
37
+ @shared_task(ignore_result=False)
38
+ def backup_task():
33
39
  dbdict = hiddify.dump_db_to_dict()
34
40
  os.makedirs('backup', exist_ok=True)
35
41
  dst = f'backup/{datetime.datetime.now().strftime("%Y_%m_%d__%H_%M_%S")}.json'
@@ -42,9 +48,12 @@ def backup():
42
48
  register_bot(True)
43
49
 
44
50
  with open(dst, 'rb') as document:
45
- for admin in AdminUser.query.filter(AdminUser.mode == AdminMode.super_admin, AdminUser.telegram_id is not None).all():
51
+ for admin in AdminUser.query.filter(AdminUser.mode == AdminMode.super_admin, AdminUser.telegram_id is not None,AdminUser.telegram_id!=0).all():
46
52
  caption = ("Backup \n" + admin_links())
47
- bot.send_document(admin.telegram_id, document, visible_file_name=dst.replace("backup/", ""), caption=caption[:min(len(caption), 1000)])
53
+ try:
54
+ bot.send_document(admin.telegram_id, document, visible_file_name=dst.replace("backup/", ""), caption=caption[:1000])
55
+ except Exception as e:
56
+ logger.exception(e)
48
57
 
49
58
 
50
59
  def all_configs():
@@ -131,7 +140,9 @@ def init_app(app):
131
140
  hiddify.add_or_update_config(key=key, value=val)
132
141
 
133
142
  return "success"
134
-
143
+ @app.cli.command()
144
+ def reset_owner_password():
145
+ AdminUser.get_super_admin().update_password("")
135
146
  @ app.cli.command()
136
147
  @ click.option("--config", "-c")
137
148
  def import_config(config):
@@ -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
- logger.error(exception)
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())
@@ -1,3 +1,5 @@
1
+ import asyncio
2
+ import time
1
3
  from flask import current_app as app, request
2
4
  from flask import g
3
5
  from flask.views import MethodView
@@ -16,7 +18,9 @@ class UpdateUserUsageApi(MethodView):
16
18
 
17
19
  def get(self):
18
20
  """System: Update User Usage"""
19
- return json.dumps(usage.update_local_usage(), indent=2)
21
+ # time.sleep(5)
22
+
23
+ return json.dumps(usage.update_local_usage_not_lock(), indent=2)
20
24
 
21
25
 
22
26
  class AllConfigsApi(MethodView):
@@ -6,7 +6,8 @@ from hiddifypanel.auth import login_required
6
6
  from hiddifypanel.models import *
7
7
  from hiddifypanel.panel import hiddify
8
8
  from hiddifypanel.drivers import user_driver
9
- from hiddifypanel.panel import hiddify
9
+
10
+
10
11
 
11
12
  from . import has_permission
12
13
  from .schema import UserSchema, PostUserSchema, PatchUserSchema, SuccessfulSchema
@@ -108,31 +108,35 @@ class AppAPI(MethodView):
108
108
  apps_data = self.__get_all_apps_dto()
109
109
  case Platform.android:
110
110
  hiddify_next_dto = self.__get_hiddify_next_app_dto()
111
+ singbox_dto = self.__get_singbox_app_dto()
111
112
  hiddifyng_dto = self.__get_hiddifyng_app_dto()
112
113
  v2rayng_dto = self.__get_v2rayng_app_dto()
113
114
  hiddify_clash_android_dto = self.__get_hiddify_clash_android_app_dto()
114
115
  nekobox_dto = self.__get_nekobox_app_dto()
115
- apps_data += ([hiddify_next_dto, hiddifyng_dto, v2rayng_dto, hiddify_clash_android_dto, nekobox_dto])
116
+ apps_data += ([hiddify_next_dto, singbox_dto, v2rayng_dto, nekobox_dto, hiddifyng_dto, hiddify_clash_android_dto])
116
117
  case Platform.windows:
117
118
  hiddify_next_dto = self.__get_hiddify_next_app_dto()
118
119
  hiddify_clash_dto = self.__get_hiddify_clash_desktop_app_dto()
119
120
  hiddifyn_dto = self.__get_hiddifyn_app_dto()
120
121
  apps_data += ([hiddify_next_dto, hiddify_clash_dto, hiddifyn_dto])
121
122
  case Platform.ios:
123
+ hiddify_next_dto = self.__get_hiddify_next_app_dto()
124
+ singbox_dto = self.__get_singbox_app_dto()
122
125
  stash_dto = self.__get_stash_app_dto()
123
126
  shadowrocket_dto = self.__get_shadowrocket_app_dto()
124
127
  foxray_dto = self.__get_foxray_app_dto()
125
128
  streisand_dto = self.__get_streisand_app_dto()
126
129
  loon_dto = self.__get_loon_app_dto()
127
- apps_data += ([streisand_dto, stash_dto, shadowrocket_dto, foxray_dto, loon_dto])
130
+ apps_data += ([hiddify_next_dto, singbox_dto, streisand_dto, stash_dto, shadowrocket_dto, foxray_dto, loon_dto])
128
131
  case Platform.linux:
129
132
  hiddify_next_dto = self.__get_hiddify_next_app_dto()
130
133
  hiddify_clash_dto = self.__get_hiddify_clash_desktop_app_dto()
131
134
  apps_data += ([hiddify_next_dto, hiddify_clash_dto,])
132
135
  case Platform.mac:
136
+ singbox_dto = self.__get_singbox_app_dto()
133
137
  hiddify_clash_dto = self.__get_hiddify_clash_desktop_app_dto()
134
138
  hiddify_next_dto = self.__get_hiddify_next_app_dto()
135
- apps_data += ([hiddify_next_dto, hiddify_clash_dto])
139
+ apps_data += ([hiddify_next_dto, singbox_dto, hiddify_clash_dto])
136
140
 
137
141
  return apps_data
138
142
 
@@ -162,11 +166,12 @@ class AppAPI(MethodView):
162
166
  loon_app_dto = self.__get_loon_app_dto()
163
167
  stash_app_dto = self.__get_stash_app_dto()
164
168
  hiddify_clash_app_dto = self.__get_hiddify_clash_desktop_app_dto()
169
+ singbox_app_dto = self.__get_singbox_app_dto()
165
170
  hiddify_next_app_dto = self.__get_hiddify_next_app_dto()
166
171
  return [
167
172
  hiddifyn_app_dto, v2rayng_app_dto, hiddifyng_app_dto, hiddify_android_app_dto,
168
173
  foxray_app_dto, shadowrocket_app_dto, streisand_app_dto,
169
- loon_app_dto, stash_app_dto, hiddify_clash_app_dto, hiddify_next_app_dto
174
+ loon_app_dto, stash_app_dto, hiddify_clash_app_dto, singbox_app_dto, hiddify_next_app_dto
170
175
  ]
171
176
 
172
177
  def __get_app_icon_url(self, app_name):
@@ -174,6 +179,8 @@ class AppAPI(MethodView):
174
179
  url = ''
175
180
  if app_name == _('app.hiddify.next.title'):
176
181
  url = base + static_url_for(filename='apps-icon/hiddify_next.ico')
182
+ elif app_name == _('app.singbox.title'):
183
+ url = base + static_url_for(filename='apps-icon/singbox.ico')
177
184
  elif app_name == _('app.hiddifyn.title'):
178
185
  url = base + static_url_for(filename='apps-icon/hiddifyn.ico')
179
186
  elif app_name == _('app.v2rayng.title'):
@@ -241,7 +248,7 @@ class AppAPI(MethodView):
241
248
 
242
249
  # make v2rayng latest version url download
243
250
  latest_url, version = get_latest_release_url(f'https://github.com/2dust/v2rayNG/')
244
- github_ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/v2rayNG_{version}.apk'
251
+ github_ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/v2rayNG_{version}_universal.apk'
245
252
  google_play_ins_url = 'https://play.google.com/store/apps/details?id=com.v2ray.ang'
246
253
  dto.install = [self.__get_app_install_dto(AppInstallType.apk, github_ins_url), self.__get_app_install_dto(AppInstallType.google_play, google_play_ins_url)]
247
254
  return dto
@@ -367,6 +374,65 @@ class AppAPI(MethodView):
367
374
 
368
375
  return dto
369
376
 
377
+ def __get_singbox_app_dto(self):
378
+ dto = AppSchema()
379
+ dto.title = _('app.singbox.title')
380
+ dto.description = _('app.singbox.description')
381
+ dto.icon_url = self.__get_app_icon_url(_('app.singbox.title'))
382
+ dto.guide_url = ''
383
+ dto.deeplink = f'sing-box://import-remote-profile/?url={self.user_panel_url}'
384
+
385
+ # availabe installatoin types
386
+ installation_types = []
387
+ if self.platform == Platform.all:
388
+ installation_types = [AppInstallType.apk, AppInstallType.google_play, AppInstallType.dmg, AppInstallType.app_store]
389
+ else:
390
+ match self.platform:
391
+ case Platform.android:
392
+ installation_types = [AppInstallType.apk, AppInstallType.google_play]
393
+ case Platform.mac:
394
+ installation_types = [AppInstallType.dmg]
395
+ case Platform.ios:
396
+ installation_types = [AppInstallType.app_store]
397
+
398
+ install_dtos = []
399
+ for install_type in installation_types:
400
+ install_dto = AppInstall()
401
+ ins_url = ''
402
+ match install_type:
403
+ case AppInstallType.apk:
404
+ latest_url, version = get_latest_release_url(f'https://github.com/SagerNet/sing-box')
405
+ ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/SFA-{version}-universal.apk'
406
+ def remove_v_from_filename(url):
407
+ parts = url.split('/')
408
+ filename = parts[-1]
409
+ new_filename = filename.replace('SFA-v', 'SFA-')
410
+ parts[-1] = new_filename
411
+ new_url = '/'.join(parts)
412
+ return new_url
413
+ ins_url = remove_v_from_filename(ins_url)
414
+ case AppInstallType.google_play:
415
+ ins_url = 'https://play.google.com/store/apps/details?id=io.nekohasekai.sfa'
416
+ case AppInstallType.dmg:
417
+ latest_url, version = get_latest_release_url(f'https://github.com/SagerNet/sing-box')
418
+ ins_url = latest_url.split('releases/')[0] + f'releases/download/{version}/SFM-{version}-universal.dmg'
419
+ def remove_v_from_filename(url):
420
+ parts = url.split('/')
421
+ filename = parts[-1]
422
+ new_filename = filename.replace('SFM-v', 'SFM-')
423
+ parts[-1] = new_filename
424
+ new_url = '/'.join(parts)
425
+ return new_url
426
+ ins_url = remove_v_from_filename(ins_url)
427
+ case AppInstallType.app_store:
428
+ ins_url = 'https://apps.apple.com/us/app/sing-box-vt/id6673731168'
429
+
430
+ install_dto = self.__get_app_install_dto(install_type, ins_url)
431
+ install_dtos.append(install_dto)
432
+
433
+ dto.install = install_dtos
434
+ return dto
435
+
370
436
  def __get_hiddify_next_app_dto(self):
371
437
  dto = AppSchema()
372
438
  dto.title = _('app.hiddify.next.title')
@@ -378,7 +444,7 @@ class AppAPI(MethodView):
378
444
  # availabe installatoin types
379
445
  installation_types = []
380
446
  if self.platform == Platform.all:
381
- installation_types = [AppInstallType.apk, AppInstallType.google_play, AppInstallType.setup, AppInstallType.portable, AppInstallType.appimage, AppInstallType.dmg]
447
+ installation_types = [AppInstallType.apk, AppInstallType.google_play, AppInstallType.setup, AppInstallType.portable, AppInstallType.appimage, AppInstallType.dmg, AppInstallType.app_store]
382
448
  else:
383
449
  match self.platform:
384
450
  case Platform.android:
@@ -389,6 +455,8 @@ class AppAPI(MethodView):
389
455
  installation_types = [AppInstallType.appimage]
390
456
  case Platform.mac:
391
457
  installation_types = [AppInstallType.dmg]
458
+ case Platform.ios:
459
+ installation_types = [AppInstallType.app_store]
392
460
 
393
461
  install_dtos = []
394
462
  for install_type in installation_types:
@@ -407,6 +475,8 @@ class AppAPI(MethodView):
407
475
  ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/Hiddify-Linux-x64.AppImage'
408
476
  case AppInstallType.dmg:
409
477
  ins_url = f'{self.hiddify_github_repo}/hiddify-next/releases/latest/download/Hiddify-MacOS.dmg'
478
+ case AppInstallType.app_store:
479
+ ins_url = 'https://apps.apple.com/us/app/hiddify-proxy-vpn/id6596777532'
410
480
 
411
481
  install_dto = self.__get_app_install_dto(install_type, ins_url)
412
482
  install_dtos.append(install_dto)
@@ -22,6 +22,9 @@ def init_app(app: APIFlask):
22
22
  app.jinja_env.globals['hutils'] = hutils
23
23
  app.jinja_env.globals['hiddify'] = hiddify
24
24
  app.jinja_env.globals['version'] = hiddifypanel.__version__
25
+ if not hiddifypanel.is_released_version:
26
+ app.jinja_env.globals['version']= "DEV"
27
+
25
28
  app.jinja_env.globals['static_url_for'] = hutils.flask.static_url_for
26
29
  app.jinja_env.globals['hurl_for'] = hutils.flask.hurl_for
27
30
  app.jinja_env.globals['_gettext'] = lambda x: print("==========", x)
@@ -40,9 +43,9 @@ def init_app(app: APIFlask):
40
43
  logger.exception(e)
41
44
  if isinstance(e, Exception):
42
45
  if hutils.flask.is_api_call(request.path):
43
- return {
46
+ return jsonify({
44
47
  'msg': str(e),
45
- }, 500
48
+ }), 500
46
49
 
47
50
  if hasattr(e, 'code') and e.code == 404:
48
51
  return jsonify({
@@ -11,6 +11,7 @@ from hiddifypanel.models import *
11
11
 
12
12
  from flask_wtf import FlaskForm
13
13
  import wtforms as wtf
14
+
14
15
  import re
15
16
 
16
17
 
@@ -22,7 +23,10 @@ class LoginForm(FlaskForm):
22
23
  '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
24
  'message': _('config.invalid_uuid')
24
25
  })
25
- submit = wtf.fields.SubmitField(_('Submit'))
26
+
27
+ password_textbox = wtf.fields.PasswordField(_(f'login.password.label'), default='',
28
+ description=_(f'login.password.description'), render_kw={ })
29
+ submit = wtf.fields.SubmitField(_('login.button'))
26
30
 
27
31
 
28
32
  class LoginView(FlaskView):
@@ -33,8 +37,9 @@ class LoginView(FlaskView):
33
37
  redirect_arg = request.args.get('redirect')
34
38
  username_arg = request.args.get('user') or ''
35
39
  if not current_account:
36
-
37
- return render_template('login.html', form=LoginForm())
40
+ form=LoginForm()
41
+ form.secret_textbox.data=form.secret_textbox.data or username_arg
42
+ return render_template('login.html', form=form)
38
43
 
39
44
  # abort(401, "Unauthorized1")
40
45
 
@@ -52,9 +57,9 @@ class LoginView(FlaskView):
52
57
  form = LoginForm()
53
58
  if form.validate_on_submit():
54
59
  uuid = form.secret_textbox.data.strip()
55
- if login_by_uuid(uuid, hutils.flask.is_admin_proxy_path()):
60
+ if login_by_uuid(uuid,form.password_textbox.data, hutils.flask.is_admin_proxy_path()):
56
61
  return redirect(f'/{g.proxy_path}/')
57
- hutils.flask.flash(_('config.validation-error'), 'danger') # type: ignore
62
+ hutils.flask.flash(_('config.invalid_uuid'), 'danger') # type: ignore
58
63
  return render_template('login.html', form=LoginForm())
59
64
 
60
65
  @ route("/l/<path:path>/")
@@ -68,7 +73,7 @@ class LoginView(FlaskView):
68
73
  redirect_arg = request.args.get('next')
69
74
 
70
75
  if not current_account or (not request.headers.get('Authorization')):
71
- username = request.authorization.username if request.authorization else ''
76
+ username = request.authorization.username if request.authorization else g.uuid
72
77
 
73
78
  loginurl = hurl_for('common_bp.LoginView:index', next=redirect_arg, user=username)
74
79
  if g.user_agent['is_browser'] and request.headers.get('Authorization') or (current_account and len(username) > 0 and current_account.username != username):
@@ -128,7 +133,8 @@ class LoginView(FlaskView):
128
133
  @ route('/<secret_uuid>/manifest.webmanifest')
129
134
  def create_pwa_manifest(self):
130
135
  domain = request.host
131
- name = (domain if hutils.flask.is_admin_panel_call() else g.account.name)
136
+ account=AdminUser.by_uuid(g.uuid)
137
+ name = (domain if hutils.flask.is_admin_panel_call() else account.name)
132
138
  return jsonify({
133
139
  "name": f"Hiddify {name}",
134
140
  "short_name": f"{name}"[:12],
@@ -136,7 +142,7 @@ class LoginView(FlaskView):
136
142
  "background_color": "#1a1b21",
137
143
  "display": "standalone",
138
144
  "scope": f"/",
139
- "start_url": hiddify.get_account_panel_link(g.account, domain) + "?pwa=true",
145
+ "start_url": hiddify.get_account_panel_link(account, domain) + "?pwa=true",
140
146
  "description": "Hiddify, for a free Internet",
141
147
  "orientation": "any",
142
148
  "icons": [
@@ -0,0 +1,32 @@
1
+ import sys
2
+ from loguru import logger
3
+
4
+ def logger_dynamic_formatter(record) -> str:
5
+ fmt = '<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>'
6
+ if record['extra']:
7
+ fmt += ' | <level>{extra}</level>'
8
+ return fmt + '\n'
9
+
10
+ def init_app(app):
11
+ init_logger(app,False)
12
+
13
+ def init_cli(app):
14
+ init_logger(app,True)
15
+
16
+
17
+ def init_logger(app, cli):
18
+ # configure logger
19
+
20
+ logger.remove()
21
+ logger.add(sys.stderr if cli else sys.stdout, format=logger_dynamic_formatter, level=app.config['STDOUT_LOG_LEVEL'],
22
+ colorize=True, catch=True, enqueue=True, diagnose=False, backtrace=True)
23
+ logger.trace('Logger initiated :)')
24
+
25
+ with app.app_context():
26
+ from hiddifypanel.models.config import hconfig,ConfigEnum
27
+ set_level(app,hconfig(ConfigEnum.log_level))
28
+
29
+
30
+ def set_level(app, level):
31
+ logger.add(app.config['HIDDIFY_CONFIG_PATH'] + "/log/system/panel.log", format=logger_dynamic_formatter, level=level,
32
+ colorize=True, catch=True, enqueue=True, diagnose=False, backtrace=True)