hiddifypanel 9.0.0.dev54__py3-none-any.whl → 9.0.0.dev61__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 (72) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/base.py +5 -4
  4. hiddifypanel/hutils/__init__.py +8 -1
  5. hiddifypanel/hutils/auth.py +94 -0
  6. hiddifypanel/hutils/auto_ip_selector.py +1 -1
  7. hiddifypanel/hutils/convert.py +14 -0
  8. hiddifypanel/hutils/encode.py +11 -0
  9. hiddifypanel/hutils/flask.py +24 -0
  10. hiddifypanel/{panel/github_issue_generator.py → hutils/github_issue.py} +104 -14
  11. hiddifypanel/hutils/json.py +24 -0
  12. hiddifypanel/hutils/random.py +19 -0
  13. hiddifypanel/hutils/utils.py +0 -161
  14. hiddifypanel/models/__init__.py +1 -0
  15. hiddifypanel/models/admin.py +53 -8
  16. hiddifypanel/models/base_account.py +31 -169
  17. hiddifypanel/models/config_enum.py +23 -1
  18. hiddifypanel/models/domain.py +2 -2
  19. hiddifypanel/models/parent_domain.py +1 -0
  20. hiddifypanel/models/user.py +105 -33
  21. hiddifypanel/models/utils.py +3 -3
  22. hiddifypanel/panel/admin/Actions.py +5 -6
  23. hiddifypanel/panel/admin/AdminstratorAdmin.py +1 -1
  24. hiddifypanel/panel/admin/Backup.py +5 -5
  25. hiddifypanel/panel/admin/ChildAdmin.py +3 -3
  26. hiddifypanel/panel/admin/Dashboard.py +12 -10
  27. hiddifypanel/panel/admin/DomainAdmin.py +10 -9
  28. hiddifypanel/panel/admin/ProxyAdmin.py +4 -6
  29. hiddifypanel/panel/admin/QuickSetup.py +11 -13
  30. hiddifypanel/panel/admin/SettingAdmin.py +20 -10
  31. hiddifypanel/panel/admin/UserAdmin.py +14 -12
  32. hiddifypanel/panel/auth.py +43 -10
  33. hiddifypanel/panel/auth_back2.py +5 -5
  34. hiddifypanel/panel/cli.py +1 -0
  35. hiddifypanel/panel/commercial/ParentDomainAdmin.py +3 -3
  36. hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +1 -0
  37. hiddifypanel/panel/commercial/restapi/v1/tgbot.py +1 -2
  38. hiddifypanel/panel/commercial/restapi/v1/tgmsg.py +37 -29
  39. hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +5 -4
  40. hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +15 -7
  41. hiddifypanel/panel/commercial/restapi/v2/user/info_api.py +7 -11
  42. hiddifypanel/panel/commercial/telegrambot/Usage.py +2 -1
  43. hiddifypanel/panel/commercial/telegrambot/admin.py +1 -0
  44. hiddifypanel/panel/common.py +12 -89
  45. hiddifypanel/panel/common_bp/login.py +12 -12
  46. hiddifypanel/panel/database.py +22 -21
  47. hiddifypanel/panel/hiddify.py +27 -29
  48. hiddifypanel/panel/importer/xui.py +2 -2
  49. hiddifypanel/panel/init_db.py +32 -13
  50. hiddifypanel/panel/usage.py +2 -1
  51. hiddifypanel/panel/user/link_maker.py +118 -15
  52. hiddifypanel/panel/user/templates/new.html +4 -2
  53. hiddifypanel/panel/user/user.py +83 -38
  54. hiddifypanel/static/new/assets/{index-bd9ba5e9.js → index-2cd90979.js} +1 -1
  55. hiddifypanel/templates/fake.html +2 -2
  56. hiddifypanel/templates/master.html +1 -1
  57. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  58. hiddifypanel/translations/en/LC_MESSAGES/messages.po +317 -189
  59. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  60. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +346 -206
  61. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  62. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +315 -195
  63. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  64. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +315 -195
  65. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  66. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +866 -2739
  67. {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/METADATA +2 -1
  68. {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/RECORD +72 -66
  69. {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/LICENSE.md +0 -0
  70. {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/WHEEL +0 -0
  71. {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/entry_points.txt +0 -0
  72. {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,7 @@ import hiddifypanel.panel.auth as auth
13
13
 
14
14
  class ProxyDetailsAdmin(AdminLTEModelView):
15
15
  column_hide_backrefs = True
16
+ can_create = False
16
17
  form_excluded_columns = ['child']
17
18
  column_exclude_list = ['child']
18
19
  # list_template = 'model/domain_list.html'
@@ -56,8 +56,7 @@ def init_app(app):
56
56
 
57
57
 
58
58
  class TGBotResource(Resource):
59
- @hiddify.api_v1_auth
60
- def post(self, admin_uuid):
59
+ def post(self, admin_uuid=None):
61
60
  try:
62
61
  if request.headers.get('content-type') == 'application/json':
63
62
  json_string = request.get_data().decode('utf-8')
@@ -1,46 +1,27 @@
1
- from flask import request
1
+ from typing import List
2
+ from flask import g, request
2
3
  from apiflask import abort
3
4
  from flask_restful import Resource
4
5
  # from flask_simplelogin import login_required
5
6
  import datetime
6
- from hiddifypanel.panel.database import db
7
+
8
+ from hiddifypanel import hutils
7
9
  from hiddifypanel.models import *
8
- from hiddifypanel.panel.auth import login_required
9
- from hiddifypanel.panel import hiddify, usage
10
10
  from .tgbot import bot
11
11
 
12
12
 
13
13
  class SendMsgResource(Resource):
14
14
  # decorators = [login_required({Role.super_admin})]
15
- @hiddify.api_v1_auth
16
- def post(self, admin_uuid):
15
+ def post(self, admin_uuid=None):
17
16
 
18
17
  if not hconfig(ConfigEnum.telegram_bot_token) or not bot:
19
18
  abort(400, 'invalid request')
20
19
 
21
20
  msg = request.json
22
- users = User.query.filter(User.telegram_id != None)
23
- id = msg['id']
24
- if type(id) == int or id.isnumeric():
25
- users = [users.filter(User.id == int(msg['id'])).first() or abort(403)]
26
- elif id == 'all':
27
- users = users.all()
28
- else:
29
- users = users.all()
30
- if id == 'expired':
31
- users = [u for u in users if not u.is_active]
32
- elif id == 'active':
33
- users = [u for u in users if u.is_active]
34
- elif id == 'offline 1h':
35
- h1 = datetime.datetime.now()-datetime.timedelta(hours=1)
36
- users = [u for u in users if u.is_active and u.last_online < h1]
37
- elif id == 'offline 1d':
38
- d1 = datetime.datetime.now()-datetime.timedelta(hours=24)
39
- users = [u for u in users if u.is_active and u.last_online < d1]
40
-
41
- elif id == 'offline 1w':
42
- d7 = datetime.datetime.now()-datetime.timedelta(days=7)
43
- users = [u for u in users if u.is_active and u.last_online < d7]
21
+ if not msg or not msg.get('id') or not msg.get('text'):
22
+ abort(400, 'invalid request')
23
+
24
+ users = self.get_users_by_identifier(msg['id'])
44
25
 
45
26
  res = {}
46
27
  for user in users:
@@ -51,9 +32,36 @@ class SendMsgResource(Resource):
51
32
  print('sending to ', user)
52
33
  bot.send_message(user.telegram_id, txt, reply_markup=keyboard)
53
34
  except Exception as e:
54
- import traceback
55
35
  res[user.uuid] = {'name': user.name, 'error': f'{e}'}
56
36
  if len(res) == 0:
57
37
  return {'msg': "success"}
58
38
  else:
59
39
  return {'msg': 'error', 'res': res}
40
+
41
+ def get_users_by_identifier(self, identifier: str) -> List[User]:
42
+ '''Returns all users that match the identifier for sending a message to them'''
43
+ # when we are here we must have g.account but ...
44
+ if not hasattr(g, 'account'):
45
+ return []
46
+ query = User.query.filter(User.added_by.in_(g.account.recursive_sub_admins_ids()))
47
+ query = query.filter(User.telegram_id != None, User.telegram_id != 0)
48
+
49
+ if hutils.convert.is_int(identifier):
50
+ return [query.filter(User.id == int(identifier)).first() or abort(404, 'The user not found')] # type: ignore
51
+ elif identifier == 'all':
52
+ return query.all()
53
+ elif identifier == 'expired':
54
+ return [u for u in query.all() if not u.is_active]
55
+ elif identifier == 'active':
56
+ return [u for u in query.all() if u.is_active]
57
+ elif identifier == 'offline 1h':
58
+ h1 = datetime.datetime.now()-datetime.timedelta(hours=1)
59
+ return [u for u in query.all() if u.is_active and u.last_online < h1]
60
+ elif identifier == 'offline 1d':
61
+ d1 = datetime.datetime.now()-datetime.timedelta(hours=24)
62
+ return [u for u in query.all() if u.is_active and u.last_online < d1]
63
+ elif identifier == 'offline 1w':
64
+ d7 = datetime.datetime.now()-datetime.timedelta(days=7)
65
+ return [u for u in query.all() if u.is_active and u.last_online < d7]
66
+ else:
67
+ return []
@@ -11,7 +11,8 @@ import user_agents
11
11
  from strenum import StrEnum
12
12
  from enum import auto
13
13
  from hiddifypanel.panel.user.user import get_common_data
14
- from hiddifypanel.hutils.utils import get_latest_release_url, do_base_64
14
+ from hiddifypanel.hutils.utils import get_latest_release_url
15
+ from hiddifypanel import hutils
15
16
  from hiddifypanel.models.role import Role
16
17
  from hiddifypanel.panel.auth import login_required
17
18
 
@@ -76,7 +77,7 @@ class AppAPI(MethodView):
76
77
  self.user_panel_encoded_url = quote_plus(self.user_panel_url)
77
78
  c = get_common_data(g.account.uuid, 'new')
78
79
  self.subscription_link_url = f"{self.user_panel_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
79
- self.subscription_link_encoded_url = do_base_64(self.subscription_link_url)
80
+ self.subscription_link_encoded_url = hutils.encode.do_base_64(self.subscription_link_url)
80
81
  domain = c['db_domain'].alias or c['db_domain'].domain
81
82
  self.profile_title = c['profile_title']
82
83
  # self.clash_all_sites = f"https://{domain}/{g.proxy_path}/clash/all.yml?mode={c['mode']}&asn={c['asn']}&name={c['asn']}_all_{domain}-{c['mode']}"
@@ -277,7 +278,7 @@ class AppAPI(MethodView):
277
278
  dto.description = _('app.foxray.description')
278
279
  dto.icon_url = self.__get_app_icon_url(_('app.foxray.title'))
279
280
  dto.guide_url = ''
280
- dto.deeplink = f'https://yiguo.dev/sub/add/?url={do_base_64(self.subscription_link_encoded_url)}#{self.profile_title}'
281
+ dto.deeplink = f'https://yiguo.dev/sub/add/?url={hutils.encode.do_base_64(self.subscription_link_encoded_url)}#{self.profile_title}'
281
282
 
282
283
  ins_url = 'https://apps.apple.com/us/app/foxray/id6448898396'
283
284
  dto.install = [self.__get_app_install_dto(AppInstallType.app_store, ins_url),]
@@ -289,7 +290,7 @@ class AppAPI(MethodView):
289
290
  dto.description = _('app.shadowrocket.description')
290
291
  dto.icon_url = self.__get_app_icon_url(_('app.shadowrocket.title'))
291
292
  dto.guide_url = 'https://www.youtube.com/watch?v=F2bC_mtbYmQ'
292
- dto.deeplink = f'sub://{do_base_64(self.user_panel_url)}'
293
+ dto.deeplink = f'sub://{hutils.encode.do_base_64(self.user_panel_url)}'
293
294
 
294
295
  ins_url = 'https://apps.apple.com/us/app/shadowrocket/id932747118'
295
296
  dto.install = [self.__get_app_install_dto(AppInstallType.app_store, ins_url),]
@@ -46,14 +46,17 @@ class AllConfigsAPI(MethodView):
46
46
  items.append(
47
47
  create_item(
48
48
  "Auto", "ALL", "ALL", "", "", "",
49
- f"{base_url}sub/?asn={c['asn']}")
49
+ # f"{base_url}sub/?asn={c['asn']}"
50
+ f"{base_url}auto/?asn={c['asn']}"
51
+ )
50
52
  )
51
53
 
52
54
  # Add Full Singbox
53
55
  items.append(
54
56
  create_item(
55
57
  "Full Singbox", "ALL", "ALL", "", "", "",
56
- f"{base_url}full-singbox.json?asn={c['asn']}"
58
+ # f"{base_url}full-singbox.json?asn={c['asn']}"
59
+ f"{base_url}singbox/?asn={c['asn']}"
57
60
  )
58
61
  )
59
62
 
@@ -61,7 +64,8 @@ class AllConfigsAPI(MethodView):
61
64
  items.append(
62
65
  create_item(
63
66
  "Clash Meta", "ALL", "ALL", "", "", "",
64
- f"clashmeta://install-config?url={base_url}clash/meta/all.yml&name=mnormal_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}"
67
+ # f"clashmeta://install-config?url={base_url}clash/meta/all.yml&name=mnormal_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}"
68
+ f"clash://install-config?url={base_url}clashmeta/?asn={c['asn']}"
65
69
  )
66
70
  )
67
71
 
@@ -69,7 +73,8 @@ class AllConfigsAPI(MethodView):
69
73
  items.append(
70
74
  create_item(
71
75
  "Clash", "ALL", "Except VLess", "", "", "",
72
- f"clash://install-config?url={base_url}clash/all.yml&name=new_normal_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}"
76
+ # f"clash://install-config?url={base_url}clash/all.yml&name=new_normal_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}"
77
+ f"clash://install-config?url={base_url}clash/?asn={c['asn']}"
73
78
  )
74
79
  )
75
80
 
@@ -78,7 +83,8 @@ class AllConfigsAPI(MethodView):
78
83
  items.append(
79
84
  create_item(
80
85
  "Singbox: SSH", "SSH", "SHH", "", "", "",
81
- f"{base_url}singbox.json?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
86
+ # f"{base_url}singbox.json?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
87
+ f"{base_url}singbox-ssh/?asn={c['asn']}"
82
88
  )
83
89
  )
84
90
 
@@ -86,7 +92,8 @@ class AllConfigsAPI(MethodView):
86
92
  items.append(
87
93
  create_item(
88
94
  "Subscription link", "ALL", "ALL", "", "", "",
89
- f"{base_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
95
+ # f"{base_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
96
+ f"{base_url}sub/?asn={c['asn']}"
90
97
  )
91
98
  )
92
99
 
@@ -94,7 +101,8 @@ class AllConfigsAPI(MethodView):
94
101
  items.append(
95
102
  create_item(
96
103
  "Subscription link b64", "ALL", "ALL", "", "", "",
97
- f"{base_url}all.txt?name=new_link_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}&base64=True"
104
+ # f"{base_url}all.txt?name=new_link_{c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}-{c['mode']}&asn={c['asn']}&mode={c['mode']}&base64=True"
105
+ f"{base_url}sub64/?asn={c['asn']}"
98
106
  )
99
107
  )
100
108
 
@@ -4,6 +4,7 @@ from apiflask import Schema
4
4
  from apiflask.fields import Integer, String, Float, URL, Enum
5
5
  from flask import g, request
6
6
  from flask import current_app as app
7
+ from hiddifypanel import hutils
7
8
  from hiddifypanel.panel.auth import login_required
8
9
  import hiddifypanel.panel.auth as auth
9
10
  from flask_babelex import gettext as _
@@ -55,11 +56,11 @@ class InfoAPI(MethodView):
55
56
  dto.profile_remaining_days = g.account.remaining_days()
56
57
  dto.profile_reset_days = g.account.days_to_reset()
57
58
  dto.telegram_bot_url = f"https://t.me/{c['bot'].username}?start={g.account.uuid}" if c['bot'] else ""
58
- dto.telegram_id = c['user'].telegram_id or 0
59
+ dto.telegram_id = c['user'].telegram_id
59
60
 
60
61
  dto.doh = f"https://{request.host}/{g.proxy_path}/dns/dns-query"
61
62
  dto.lang = (c['user'].lang) or Lang(hconfig(ConfigEnum.lang))
62
- dto.brand_icon_url = "" if hconfig(ConfigEnum.branding_title) else hiddify.static_url_for(filename="images/hiddify.png")
63
+ dto.brand_icon_url = "" if hconfig(ConfigEnum.branding_title) else hutils.flask.static_url_for(filename="images/hiddify.png")
63
64
  # with force_locale("fa"):
64
65
  dto.admin_message_html = hconfig(ConfigEnum.branding_freetext) or _("Join our Hiddify Telegram channel to get the latest updates on Hiddify.")
65
66
  if not hconfig(ConfigEnum.branding_freetext) and auth.admin_session_is_exist():
@@ -69,16 +70,11 @@ class InfoAPI(MethodView):
69
70
  return dto
70
71
 
71
72
  @app.input(UserInfoChangableSchema, arg_name='data')
72
- def patch(self, data):
73
- if data['telegram_id']:
74
- try:
75
- tg_id = int(data['telegram_id'])
76
- except:
77
- return {'message': 'The telegram id field is invalid'}
78
-
73
+ def patch(self, data: UserInfoChangableSchema):
74
+ if data['telegram_id'] and hutils.convert.is_int(data['telegram_id']):
79
75
  user = User.by_uuid(g.account.uuid)
80
- if user.telegram_id != tg_id:
81
- user.telegram_id = tg_id
76
+ if user.telegram_id != data['telegram_id']:
77
+ user.telegram_id = data['telegram_id']
82
78
  db.session.commit()
83
79
 
84
80
  if data['language']:
@@ -1,3 +1,4 @@
1
+ from hiddifypanel.panel import hiddify
1
2
  from telebot import types
2
3
  from flask_babelex import gettext as _
3
4
  from flask import current_app as app
@@ -52,7 +53,7 @@ def get_usage_msg(uuid, domain=None):
52
53
  reset_day = user_data['reset_day']
53
54
 
54
55
  domain = domain or get_panel_domains()[0]
55
- user_link = hiddify.get_account_panel_link(user,domain.domain)
56
+ user_link = hiddify.get_account_panel_link(user, domain.domain)
56
57
  msg = f"""{_('<a href="%(user_link)s"> %(user)s</a>',user_link=user_link ,user=user.name if user.name != "default" else "")}\n\n"""
57
58
 
58
59
  msg += f"""{_('user.home.usage.title')} {round(user.current_usage_GB, 3)}GB <b>{_('user.home.usage.from')}</b> {user.usage_limit_GB}GB {_('user.home.usage.monthly') if user.monthly else ''}\n"""
@@ -1,3 +1,4 @@
1
+ from hiddifypanel.panel import hiddify
1
2
  from . import bot
2
3
 
3
4
  from telebot import types
@@ -6,11 +6,10 @@ from flask import g, send_from_directory, session
6
6
  from flask_babelex import gettext as _
7
7
  import hiddifypanel
8
8
  from hiddifypanel.models import *
9
- from hiddifypanel.panel import hiddify, github_issue_generator
10
- from sys import version as python_version
11
- from platform import platform
12
- import hiddifypanel.hutils as hutils
9
+ from hiddifypanel.panel import hiddify
10
+ from hiddifypanel import hutils
13
11
  import hiddifypanel.panel.auth as auth
12
+ from hiddifypanel.panel.auth import current_account
14
13
  from apiflask import APIFlask, HTTPError, abort
15
14
 
16
15
 
@@ -48,7 +47,7 @@ def init_app(app: APIFlask):
48
47
  trace = traceback.format_exc()
49
48
 
50
49
  # Create github issue link
51
- issue_link = generate_github_issue_link_for_500_error(e, trace)
50
+ issue_link = hutils.github_issue.generate_github_issue_link_for_500_error(e, trace)
52
51
 
53
52
  return render_template('500.html', error=e, trace=trace, has_update=has_update, last_version=last_version, issue_link=issue_link), 500
54
53
 
@@ -61,7 +60,7 @@ def init_app(app: APIFlask):
61
60
  trace = traceback.format_exc()
62
61
 
63
62
  # Create github issue link
64
- issue_link = generate_github_issue_link_for_500_error(e, trace)
63
+ issue_link = hutils.github_issue.generate_github_issue_link_for_500_error(e, trace)
65
64
 
66
65
  last_version = hiddify.get_latest_release_version('hiddify-panel') # TODO: add dev update check
67
66
  if "T" in hiddifypanel.__version__:
@@ -75,16 +74,6 @@ def init_app(app: APIFlask):
75
74
 
76
75
  return render_template('error.html', error=e), e.status_code
77
76
 
78
- def generate_github_issue_link(title, issue_body):
79
- opts = {
80
- "user": 'hiddify',
81
- "repo": 'Hiddify-Manager',
82
- "title": title,
83
- "body": issue_body,
84
- }
85
- issue_link = str(github_issue_generator.IssueUrl(opts).get_url())
86
- return issue_link
87
-
88
77
  # @app.spec_processor
89
78
  # def set_default_path_values(spec):
90
79
  # # for path in spec['paths'].values():
@@ -100,8 +89,7 @@ def init_app(app: APIFlask):
100
89
  @app.url_defaults
101
90
  def add_proxy_path_user(endpoint, values):
102
91
  if 'proxy_path' not in values:
103
-
104
- if hasattr(g, 'account') and isinstance(g.account, AdminUser):
92
+ if hiddify.is_admin_role(g.account):
105
93
  values['proxy_path'] = hconfig(ConfigEnum.proxy_path_admin)
106
94
  # elif 'static' in endpoint:
107
95
  # values['proxy_path'] = hconfig(ConfigEnum.proxy_path)
@@ -114,8 +102,8 @@ def init_app(app: APIFlask):
114
102
 
115
103
  if hiddify.is_api_v1_call(endpoint=endpoint) and 'admin_uuid' not in values:
116
104
  values['admin_uuid'] = AdminUser.get_super_admin_uuid()
117
- # if 'secret_uuid' not in values:
118
- # values['secret_uuid'] = AdminUser.get_super_admin_uuid()
105
+ # if 'secret_uuid' not in values and g.account and ".webmanifest" in request.path:
106
+ # values['secret_uuid'] = g.account.uuid
119
107
 
120
108
  @app.route("/<proxy_path>/videos/<file>")
121
109
  @app.doc(hide=True)
@@ -145,7 +133,7 @@ def init_app(app: APIFlask):
145
133
  # if user_agent.is_bot:
146
134
  # abort(400, "invalid")
147
135
 
148
- # uuid = hutils.utils.get_uuid_from_url_path(request.path)
136
+ # uuid = hutils.auth.get_uuid_from_url_path(request.path)
149
137
  # account = User.by_uuid(uuid) or AdminUser.by_uuid(uuid) or abort(400, 'invalid request2')
150
138
 
151
139
  # admin_proxy_path = hconfig(ConfigEnum.proxy_path_admin)
@@ -162,7 +150,7 @@ def init_app(app: APIFlask):
162
150
  # else:
163
151
  # return abort(400, 'invalid request 1')
164
152
 
165
- # new_link = hutils.utils.add_basic_auth_to_url(new_link, account.username, account.password)
153
+ # new_link = hutils.auth.add_basic_auth_to_url(new_link, account.username, account.password)
166
154
 
167
155
  # if user_agent.browser:
168
156
  # return render_template('redirect_to_new_format.html', new_link=new_link)
@@ -172,6 +160,7 @@ def init_app(app: APIFlask):
172
160
  # return redirect(new_link, 302)
173
161
  @app.before_request
174
162
  def set_default_values():
163
+ g.account = current_account
175
164
  g.user_agent = hiddify.get_user_agent()
176
165
 
177
166
  @app.before_request
@@ -217,70 +206,4 @@ def init_app(app: APIFlask):
217
206
  else:
218
207
  g.bot = None
219
208
 
220
- def github_issue_details():
221
- details = {
222
- 'hiddify_version': f'{hiddifypanel.__version__}',
223
- 'python_version': f'{python_version}',
224
- 'os_details': f'{platform()}',
225
- 'user_agent': request.user_agent
226
- }
227
- return details
228
-
229
- def generate_github_issue_link_for_500_error(error, traceback, remove_sensetive_data=True, remove_unrelated_traceback_datails=True):
230
-
231
- def remove_sensetive_data_from_github_issue_link(issue_link):
232
- if hasattr(g, 'account') and hasattr(g.account, 'uuid') and g.account.uuid:
233
- issue_link.replace(f'{g.account.uuid}', '*******************')
234
-
235
- issue_link.replace(request.host, '**********')
236
- issue_link.replace(hconfig(ConfigEnum.proxy_path), '**********')
237
- issue_link.replace(hconfig(ConfigEnum.proxy_path_admin), '**********')
238
- issue_link.replace(hconfig(ConfigEnum.proxy_path_client), '**********')
239
-
240
- def remove_unrelated_traceback_details(stacktrace: str):
241
- lines = stacktrace.splitlines()
242
- if len(lines) < 1:
243
- return ""
244
-
245
- output = ''
246
- skip_next_line = False
247
- for i, line in enumerate(lines):
248
- if i == 0:
249
- output += line + '\n'
250
- continue
251
- if skip_next_line == True:
252
- skip_next_line = False
253
- continue
254
- if line.strip().startswith('File'):
255
- if 'hiddify' in line.lower():
256
- output += line + '\n'
257
- if len(lines) > i+1:
258
- output += lines[i + 1] + '\n'
259
- skip_next_line = True
260
-
261
- return output
262
-
263
- if remove_unrelated_traceback_datails:
264
- traceback = remove_unrelated_traceback_details(traceback)
265
-
266
- issue_details = github_issue_details()
267
-
268
- issue_body = render_template('github_issue_body.j2', issue_details=issue_details, error=error, traceback=traceback)
269
-
270
- # Create github issue link
271
- issue_link = generate_github_issue_link(f"Internal server error: {error.name if hasattr(error,'name') and error.name != None and error.name else 'Unknown'}", issue_body)
272
-
273
- if remove_sensetive_data:
274
- remove_sensetive_data_from_github_issue_link(issue_link)
275
-
276
- return issue_link
277
-
278
- def generate_github_issue_link_for_admin_sidebar():
279
-
280
- issue_body = render_template('github_issue_body.j2', issue_details=github_issue_details())
281
-
282
- # Create github issue link
283
- issue_link = generate_github_issue_link('Please fill the title properly', issue_body)
284
- return issue_link
285
-
286
- app.jinja_env.globals['generate_github_issue_link_for_admin_sidebar'] = generate_github_issue_link_for_admin_sidebar
209
+ app.jinja_env.globals['generate_github_issue_link_for_admin_sidebar'] = hutils.github_issue.generate_github_issue_link_for_admin_sidebar
@@ -1,6 +1,7 @@
1
1
  from flask_classful import FlaskView, route
2
+ from hiddifypanel import hutils
2
3
  from hiddifypanel.panel.auth import login_required, current_account, login_user, logout_user, login_by_uuid
3
- from flask import redirect, request, g, url_for, render_template, flash
4
+ from flask import redirect, request, g, url_for, render_template, flash, jsonify
4
5
  from flask import current_app as app
5
6
  from flask_babelex import lazy_gettext as _
6
7
  from apiflask import abort
@@ -54,7 +55,7 @@ class LoginView(FlaskView):
54
55
  uuid = form.secret_textbox.data.strip()
55
56
  if login_by_uuid(uuid, hiddify.is_admin_proxy_path()):
56
57
  return redirect(f'/{g.proxy_path}/')
57
- flash(_('config.validation-error'), 'danger')
58
+ hutils.flask.flash(_('config.validation-error'), 'danger') # type: ignore
58
59
  return render_template('login.html', form=LoginForm())
59
60
 
60
61
  @route("/l/")
@@ -66,10 +67,10 @@ class LoginView(FlaskView):
66
67
 
67
68
  loginurl = url_for('common_bp.LoginView:index', next=redirect_arg, user=username)
68
69
  if g.user_agent['is_browser'] and request.headers.get('Authorization') or (current_account and len(username) > 0 and current_account.username != username):
69
- flash(_('Incorrect Password'), 'error')
70
+ hutils.flask.flash(_('Incorrect Password'), 'error') # type: ignore
70
71
  logout_user()
71
- g.account = None
72
- # flash(request.authorization.username, 'error')
72
+ g.__account_store = None
73
+ # hutils.flask.flash(request.authorization.username, 'error')
73
74
  return redirect(loginurl)
74
75
 
75
76
  return render_template("redirect.html", url=loginurl), 401
@@ -90,13 +91,13 @@ class LoginView(FlaskView):
90
91
 
91
92
  # def uuid(self, uuid, path=''):
92
93
  # proxy_path = hiddify.get_proxy_path_from_url(request.url)
93
- # g.account = None
94
+ # g.__account_store = None
94
95
  # uuid = str(uuid)
95
96
  # if proxy_path == hconfig(ConfigEnum.proxy_path_client):
96
- # g.account = User.by_uuid(uuid)
97
+ # g.__account_store = User.by_uuid(uuid)
97
98
  # path = f'client/{path}'
98
99
  # elif proxy_path == hconfig(ConfigEnum.proxy_path_admin):
99
- # g.account = AdminUser.by_uuid(uuid)
100
+ # g.__account_store = AdminUser.by_uuid(uuid)
100
101
  # if not g.account:
101
102
  # abort(403)
102
103
  # if not g.user_agent['is_browser'] and proxy_path == hconfig(ConfigEnum.proxy_path_client):
@@ -118,8 +119,7 @@ class LoginView(FlaskView):
118
119
 
119
120
  # return redirect(f"/{proxy_path}/{path}")
120
121
 
121
- @route('/manifest.webmanifest')
122
- @login_required()
122
+ @route('/<secret_uuid>/manifest.webmanifest')
123
123
  def create_pwa_manifest(self):
124
124
  domain = request.host
125
125
  name = (domain if hiddify.is_admin_panel_call() else g.account.name)
@@ -130,12 +130,12 @@ class LoginView(FlaskView):
130
130
  "background_color": "#1a1b21",
131
131
  "display": "standalone",
132
132
  "scope": f"/",
133
- "start_url": hiddify.get_account_panel_link(g.account, domain) +"?pwa=true",
133
+ "start_url": hiddify.get_account_panel_link(g.account, domain) + "?pwa=true",
134
134
  "description": "Hiddify, for a free Internet",
135
135
  "orientation": "any",
136
136
  "icons": [
137
137
  {
138
- "src": hiddify.static_url_for(filename='images/hiddify-dark.png'),
138
+ "src": hutils.flask.static_url_for(filename='images/hiddify-dark.png'),
139
139
  "sizes": "512x512",
140
140
  "type": "image/png",
141
141
  "purpose": "maskable any"
@@ -6,7 +6,8 @@ import re
6
6
  import os
7
7
 
8
8
  db = SQLAlchemy()
9
- db.UUID = UUIDType
9
+ db.UUID = UUIDType # type: ignore
10
+
10
11
 
11
12
  def init_app(app):
12
13
  app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
@@ -14,23 +15,23 @@ def init_app(app):
14
15
 
15
16
  # db.create_all(app)
16
17
  # app.jinja_env.globals['get_locale'] = get_locale
17
- def init_migration(app):
18
- migrate = flask_migrate.Migrate(app,db)
19
- if os.path.isdir(migrate.directory):
20
- return
21
- flask_migrate.init()
22
- def migrate():
23
- try_again = False
24
- try:
25
- # run flask_migrate.migrate function without its decorator to catch function error in try statement
26
- flask_migrate.migrate.__wrapped__()
27
- except Exception as err:
28
- err_str = str(err)
29
- if err_str == 'Target database is not up to date.':
30
- flask_migrate.stamp()
31
- elif err_str.startswith("Can't locate revision identified by"):
32
- rev_id = re.findall(" '(.*)'$",err_str)[0].replace("'","'").strip()
33
- flask_migrate.revision(rev_id=rev_id)
34
- finally:
35
- if try_again:
36
- flask_migrate.migrate()
18
+ # def init_migration(app):
19
+ # migrate = flask_migrate.Migrate(app,db)
20
+ # if os.path.isdir(migrate.directory):
21
+ # return
22
+ # flask_migrate.init()
23
+ # def migrate():
24
+ # try_again = False
25
+ # try:
26
+ # # run flask_migrate.migrate function without its decorator to catch function error in try statement
27
+ # flask_migrate.migrate.__wrapped__()
28
+ # except Exception as err:
29
+ # err_str = str(err)
30
+ # if err_str == 'Target database is not up to date.':
31
+ # flask_migrate.stamp()
32
+ # elif err_str.startswith("Can't locate revision identified by"):
33
+ # rev_id = re.findall(" '(.*)'$",err_str)[0].replace("'","'").strip()
34
+ # flask_migrate.revision(rev_id=rev_id)
35
+ # finally:
36
+ # if try_again:
37
+ # flask_migrate.migrate()