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.
- hiddifypanel/VERSION +1 -1
- hiddifypanel/VERSION.py +2 -2
- hiddifypanel/base.py +5 -4
- hiddifypanel/hutils/__init__.py +8 -1
- hiddifypanel/hutils/auth.py +94 -0
- hiddifypanel/hutils/auto_ip_selector.py +1 -1
- hiddifypanel/hutils/convert.py +14 -0
- hiddifypanel/hutils/encode.py +11 -0
- hiddifypanel/hutils/flask.py +24 -0
- hiddifypanel/{panel/github_issue_generator.py → hutils/github_issue.py} +104 -14
- hiddifypanel/hutils/json.py +24 -0
- hiddifypanel/hutils/random.py +19 -0
- hiddifypanel/hutils/utils.py +0 -161
- hiddifypanel/models/__init__.py +1 -0
- hiddifypanel/models/admin.py +53 -8
- hiddifypanel/models/base_account.py +31 -169
- hiddifypanel/models/config_enum.py +23 -1
- hiddifypanel/models/domain.py +2 -2
- hiddifypanel/models/parent_domain.py +1 -0
- hiddifypanel/models/user.py +105 -33
- hiddifypanel/models/utils.py +3 -3
- hiddifypanel/panel/admin/Actions.py +5 -6
- hiddifypanel/panel/admin/AdminstratorAdmin.py +1 -1
- hiddifypanel/panel/admin/Backup.py +5 -5
- hiddifypanel/panel/admin/ChildAdmin.py +3 -3
- hiddifypanel/panel/admin/Dashboard.py +12 -10
- hiddifypanel/panel/admin/DomainAdmin.py +10 -9
- hiddifypanel/panel/admin/ProxyAdmin.py +4 -6
- hiddifypanel/panel/admin/QuickSetup.py +11 -13
- hiddifypanel/panel/admin/SettingAdmin.py +20 -10
- hiddifypanel/panel/admin/UserAdmin.py +14 -12
- hiddifypanel/panel/auth.py +43 -10
- hiddifypanel/panel/auth_back2.py +5 -5
- hiddifypanel/panel/cli.py +1 -0
- hiddifypanel/panel/commercial/ParentDomainAdmin.py +3 -3
- hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +1 -0
- hiddifypanel/panel/commercial/restapi/v1/tgbot.py +1 -2
- hiddifypanel/panel/commercial/restapi/v1/tgmsg.py +37 -29
- hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +5 -4
- hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +15 -7
- hiddifypanel/panel/commercial/restapi/v2/user/info_api.py +7 -11
- hiddifypanel/panel/commercial/telegrambot/Usage.py +2 -1
- hiddifypanel/panel/commercial/telegrambot/admin.py +1 -0
- hiddifypanel/panel/common.py +12 -89
- hiddifypanel/panel/common_bp/login.py +12 -12
- hiddifypanel/panel/database.py +22 -21
- hiddifypanel/panel/hiddify.py +27 -29
- hiddifypanel/panel/importer/xui.py +2 -2
- hiddifypanel/panel/init_db.py +32 -13
- hiddifypanel/panel/usage.py +2 -1
- hiddifypanel/panel/user/link_maker.py +118 -15
- hiddifypanel/panel/user/templates/new.html +4 -2
- hiddifypanel/panel/user/user.py +83 -38
- hiddifypanel/static/new/assets/{index-bd9ba5e9.js → index-2cd90979.js} +1 -1
- hiddifypanel/templates/fake.html +2 -2
- hiddifypanel/templates/master.html +1 -1
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +317 -189
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +346 -206
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +315 -195
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +315 -195
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +866 -2739
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/METADATA +2 -1
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/RECORD +72 -66
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/WHEEL +0 -0
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-9.0.0.dev54.dist-info → hiddifypanel-9.0.0.dev61.dist-info}/top_level.txt +0 -0
@@ -56,8 +56,7 @@ def init_app(app):
|
|
56
56
|
|
57
57
|
|
58
58
|
class TGBotResource(Resource):
|
59
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
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
|
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
|
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 !=
|
81
|
-
user.telegram_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"""
|
hiddifypanel/panel/common.py
CHANGED
@@ -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
|
10
|
-
from
|
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'] =
|
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.
|
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.
|
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
|
-
|
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.
|
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.
|
94
|
+
# g.__account_store = None
|
94
95
|
# uuid = str(uuid)
|
95
96
|
# if proxy_path == hconfig(ConfigEnum.proxy_path_client):
|
96
|
-
# g.
|
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.
|
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('
|
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":
|
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"
|
hiddifypanel/panel/database.py
CHANGED
@@ -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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def migrate():
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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()
|