hiddifypanel 9.0.0.dev20__py3-none-any.whl → 9.0.0.dev22__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 (25) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/models/domain.py +3 -2
  4. hiddifypanel/panel/admin/Dashboard.py +6 -1
  5. hiddifypanel/panel/admin/DomainAdmin.py +5 -2
  6. hiddifypanel/panel/common.py +94 -50
  7. hiddifypanel/panel/hiddify.py +14 -0
  8. hiddifypanel/panel/user/user.py +160 -21
  9. hiddifypanel/templates/admin-layout.html +7 -1
  10. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  11. hiddifypanel/translations/en/LC_MESSAGES/messages.po +98 -74
  12. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  13. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +96 -49
  14. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  15. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +104 -45
  16. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  17. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +202 -361
  18. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  19. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +88 -54
  20. {hiddifypanel-9.0.0.dev20.dist-info → hiddifypanel-9.0.0.dev22.dist-info}/METADATA +1 -1
  21. {hiddifypanel-9.0.0.dev20.dist-info → hiddifypanel-9.0.0.dev22.dist-info}/RECORD +25 -25
  22. {hiddifypanel-9.0.0.dev20.dist-info → hiddifypanel-9.0.0.dev22.dist-info}/LICENSE.md +0 -0
  23. {hiddifypanel-9.0.0.dev20.dist-info → hiddifypanel-9.0.0.dev22.dist-info}/WHEEL +0 -0
  24. {hiddifypanel-9.0.0.dev20.dist-info → hiddifypanel-9.0.0.dev22.dist-info}/entry_points.txt +0 -0
  25. {hiddifypanel-9.0.0.dev20.dist-info → hiddifypanel-9.0.0.dev22.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION CHANGED
@@ -1 +1 @@
1
- 9.0.0.dev20
1
+ 9.0.0.dev22
hiddifypanel/VERSION.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__='9.0.0.dev20'
1
+ __version__='9.0.0.dev22'
2
2
  from datetime import datetime
3
- __release_date__= datetime.strptime('2023-10-24','%Y-%m-%d')
3
+ __release_date__= datetime.strptime('2023-10-30','%Y-%m-%d')
@@ -8,6 +8,7 @@ from strenum import StrEnum
8
8
  from hiddifypanel.panel.database import db
9
9
  from .config import hconfig
10
10
  from .config_enum import ConfigEnum
11
+ from sqlalchemy.orm import backref
11
12
 
12
13
 
13
14
  class DomainType(StrEnum):
@@ -47,7 +48,7 @@ class Domain(db.Model, SerializerMixin):
47
48
  show_domains = db.relationship('Domain', secondary=ShowDomain,
48
49
  primaryjoin=id == ShowDomain.c.domain_id,
49
50
  secondaryjoin=id == ShowDomain.c.related_id,
50
- # backref=backref('show_domains', lazy='dynamic')
51
+ backref=backref('showed_by_domains', lazy='dynamic')
51
52
  )
52
53
 
53
54
  def __repr__(self):
@@ -76,7 +77,7 @@ class Domain(db.Model, SerializerMixin):
76
77
 
77
78
  @property
78
79
  def need_valid_ssl(self):
79
- return self.mode in ['direct', 'cdn', 'worker', 'relay', 'auto_cdn_ip', 'old_xtls_direct', 'sub_link_only']
80
+ return self.mode in [DomainType.direct, DomainType.cdn, DomainType.worker, DomainType.relay, DomainType.auto_cdn_ip, DomainType.old_xtls_direct, DomainType.sub_link_only]
80
81
 
81
82
  @property
82
83
  def port_index(self):
@@ -1,4 +1,6 @@
1
1
  import datetime
2
+ import os
3
+ import re
2
4
 
3
5
  from flask import render_template, url_for, request, jsonify, g, redirect, abort
4
6
  from flask_babelex import lazy_gettext as _
@@ -23,7 +25,7 @@ class Dashboard(FlaskView):
23
25
  ))
24
26
 
25
27
  def index(self):
26
- print(hconfig(ConfigEnum.first_setup))
28
+
27
29
  if hconfig(ConfigEnum.first_setup):
28
30
  return redirect(url_for("admin.QuickSetup:index"))
29
31
  if hiddifypanel.__release_date__ + datetime.timedelta(days=20) < datetime.datetime.now():
@@ -70,6 +72,8 @@ class Dashboard(FlaskView):
70
72
  d = domains[0]
71
73
  u = def_user.uuid
72
74
  flash((_('It seems that you have not created any users yet. Default user link: %(default_link)s', default_link=hiddify.get_user_link(u, d))), 'secondary')
75
+ if hiddify.is_ssh_password_authentication_enabled():
76
+ flash(_('ssh.password-login.warning.'), "warning")
73
77
 
74
78
  # except:
75
79
  # flash((_('Error!!!')),'info')
@@ -85,3 +89,4 @@ class Dashboard(FlaskView):
85
89
  db.session.commit()
86
90
  flash(_("child has been removed!"), "success")
87
91
  return self.index()
92
+
@@ -1,6 +1,6 @@
1
1
  from hiddifypanel.models import *
2
2
  import re
3
-
3
+ from hiddifypanel.panel.database import db
4
4
  from flask import Markup
5
5
  from flask import g, flash
6
6
  from flask_babelex import gettext as __
@@ -248,6 +248,9 @@ class DomainAdmin(AdminLTEModelView):
248
248
  def on_model_delete(self, model):
249
249
  if len(Domain.query.all()) <= 1:
250
250
  raise ValidationError(f"at least one domain should exist")
251
+ # ShowDomain.query.filter_by(related_id == model.id).delete()
252
+ model.showed_by_domains = []
253
+ # db.session.commit()
251
254
  hiddify.flash_config_success(restart_mode='apply', domain_changed=True)
252
255
 
253
256
  def after_model_delete(self, model):
@@ -260,7 +263,7 @@ class DomainAdmin(AdminLTEModelView):
260
263
  set_hconfig(ConfigEnum.first_setup, False)
261
264
  # if hconfig(ConfigEnum.parent_panel):
262
265
  # hiddify_api.sync_child_to_parent()
263
- if model.mode in [DomainType.direct, DomainType.cdn, DomainType.worker, DomainType.relay, DomainType.auto_cdn_ip, DomainType.old_xtls_direct, DomainType.sub_link_only]:
266
+ if model.need_valid_ssl():
264
267
  hiddify.exec_command(f"sudo /opt/hiddify-manager/acme.sh/get_cert.sh {model.domain}")
265
268
 
266
269
  def is_accessible(self):
@@ -23,27 +23,9 @@ def init_app(app):
23
23
  def internal_server_error(e):
24
24
  if not hasattr(e, 'code') or e.code == 500:
25
25
  trace = traceback.format_exc()
26
- trace = remove_unrelated_stacktrace_details(trace)
27
- issue_body = f"""
28
- # Internal Error Stacktrace:
29
- **Error Message**: {str(e)}
30
- ```
31
- {trace}
32
- ```
33
- ## Details:
34
- **Hiddify Version**: {hiddifypanel.__version__}
35
- **Python Version**: {python_version}
36
- **OS**: {platform()}
37
- """
38
-
39
- # Add user agent if exists
40
- if hasattr(g, 'user_agent') and str(g.user_agent):
41
- issue_body += f"**User Agent**: {str(g.user_agent)}"
42
26
 
43
27
  # Create github issue link
44
- issue_link = generate_github_issue_link(e, issue_body)
45
- # Remove proxy path and uuid from stacktrace details
46
- remove_sensetive_data_from_github_issue_link(issue_link)
28
+ issue_link = generate_github_issue_link_for_500_error(e, trace)
47
29
 
48
30
  last_version = hiddify.get_latest_release_version('hiddifypanel')
49
31
  has_update = "T" not in hiddifypanel.__version__ and "dev" not in hiddifypanel.__version__ and f'{last_version}' != hiddifypanel.__version__
@@ -53,40 +35,11 @@ def init_app(app):
53
35
 
54
36
  return render_template('error.html', error=e), e.code
55
37
 
56
- def remove_sensetive_data_from_github_issue_link(issue_link):
57
- if hasattr(g, 'user_uuid') and g.user_uuid:
58
- issue_link.replace(f'{g.user_uuid}', '*******************')
59
- if hconfig(ConfigEnum.proxy_path) and hconfig(ConfigEnum.proxy_path):
60
- issue_link.replace(hconfig(ConfigEnum.proxy_path), '**********')
61
-
62
- def remove_unrelated_stacktrace_details(stacktrace: str):
63
- lines = stacktrace.splitlines()
64
- if len(lines) < 1:
65
- return ""
66
-
67
- output = ''
68
- skip_next_line = False
69
- for i, line in enumerate(lines):
70
- if i == 0:
71
- output += line + '\n'
72
- continue
73
- if skip_next_line == True:
74
- skip_next_line = False
75
- continue
76
- if line.strip().startswith('File'):
77
- if 'hiddify' in line.lower():
78
- output += line + '\n'
79
- if i < len(lines)-1:
80
- output += lines[i + 1] + '\n'
81
- skip_next_line = True
82
-
83
- return output
84
-
85
- def generate_github_issue_link(e, issue_body):
38
+ def generate_github_issue_link(title, issue_body):
86
39
  opts = {
87
40
  "user": 'hiddify',
88
41
  "repo": 'Hiddify-Manager',
89
- "title": f"Internal server error: {e}",
42
+ "title": title,
90
43
  "body": issue_body,
91
44
  }
92
45
  issue_link = str(github_issue_generator.IssueUrl(opts).get_url())
@@ -169,4 +122,95 @@ def init_app(app):
169
122
  if (not telegrambot.bot) or (not telegrambot.bot.username):
170
123
  telegrambot.register_bot()
171
124
  g.bot = telegrambot.bot
125
+ else:
126
+ g.bot = None
127
+
172
128
  # print(g.user)
129
+
130
+ def github_issue_details():
131
+ details = {
132
+ 'hiddify_version': f'{hiddifypanel.__version__}',
133
+ 'python_version': f'{python_version}',
134
+ 'os_details': f'{platform()}',
135
+ 'user_agent': 'Unknown'
136
+ }
137
+ if hasattr(g, 'user_agent') and str(g.user_agent):
138
+ details['user_agent'] = g.user_agent.ua_string
139
+ return details
140
+
141
+ def generate_github_issue_link_for_500_error(error, traceback, remove_sensetive_data=True, remove_unrelated_traceback_datails=True):
142
+
143
+ def remove_sensetive_data_from_github_issue_link(issue_link):
144
+ if hasattr(g, 'user_uuid') and g.user_uuid:
145
+ issue_link.replace(f'{g.user_uuid}', '*******************')
146
+ if hconfig(ConfigEnum.proxy_path) and hconfig(ConfigEnum.proxy_path):
147
+ issue_link.replace(hconfig(ConfigEnum.proxy_path), '**********')
148
+
149
+ def remove_unrelated_traceback_details(stacktrace: str):
150
+ lines = stacktrace.splitlines()
151
+ if len(lines) < 1:
152
+ return ""
153
+
154
+ output = ''
155
+ skip_next_line = False
156
+ for i, line in enumerate(lines):
157
+ if i == 0:
158
+ output += line + '\n'
159
+ continue
160
+ if skip_next_line == True:
161
+ skip_next_line = False
162
+ continue
163
+ if line.strip().startswith('File'):
164
+ if 'hiddify' in line.lower():
165
+ output += line + '\n'
166
+ output += lines[i + 1] + '\n'
167
+ skip_next_line = True
168
+
169
+ return output
170
+
171
+ if remove_unrelated_traceback_datails:
172
+ traceback = remove_unrelated_traceback_details(traceback)
173
+
174
+ gd = github_issue_details()
175
+
176
+ issue_body = f"""
177
+ # Internal Error Stacktrace:
178
+ **Error Message**: {str(error)}
179
+ ```
180
+ {traceback}
181
+ ```
182
+ ## Details:
183
+ **Hiddify Version**: {gd['hiddify_version']}
184
+ **Python Version**: {gd['python_version']}
185
+ **OS**: {gd['os_details']}
186
+ **User Agent**: {gd['user_agent'] if gd['user_agent'] else "Unknown"}
187
+ """
188
+
189
+ # Create github issue link
190
+ 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)
191
+
192
+ if remove_sensetive_data:
193
+ remove_sensetive_data_from_github_issue_link(issue_link)
194
+
195
+ return issue_link
196
+
197
+ def generate_github_issue_link_for_admin_sidebar():
198
+
199
+ gd = github_issue_details()
200
+
201
+ issue_body = f"""
202
+ # Bug:
203
+ **Description**: "Describe your problem"
204
+
205
+ ## Details:
206
+ **Hiddify Version**: {gd['hiddify_version']}
207
+ **Python Version**: {gd['python_version']}
208
+ **OS**: {gd['os_details']}
209
+ **User Agent**: {gd['user_agent'] if gd['user_agent'] else "Unknown"}
210
+ """
211
+
212
+ # Create github issue link
213
+ issue_link = generate_github_issue_link('Please fill the title properly', issue_body)
214
+ return issue_link
215
+
216
+ app.jinja_env.globals['generate_github_issue_link_for_admin_sidebar'] = generate_github_issue_link_for_admin_sidebar
@@ -724,3 +724,17 @@ def __parse_user_agent(ua):
724
724
  if res["is_browser"]:
725
725
  res['app'] = uaa.browser.family
726
726
  return res
727
+
728
+
729
+ def is_ssh_password_authentication_enabled():
730
+ if os.path.isfile('/etc/ssh/sshd_config'):
731
+ content = ''
732
+ with open('/etc/ssh/sshd_config', 'r') as f:
733
+ for line in f.readlines():
734
+ line = line.strip()
735
+ if line.startswith('#'):
736
+ continue
737
+ if re.search("^PasswordAuthentication\s+no", line, re.IGNORECASE):
738
+ return False
739
+
740
+ return True
@@ -7,7 +7,6 @@ import datetime
7
7
  from hiddifypanel.models import *
8
8
  from hiddifypanel.panel.database import db
9
9
  from hiddifypanel.panel import hiddify, clean_ip
10
-
11
10
  from . import link_maker
12
11
  from flask_classful import FlaskView, route
13
12
  import random
@@ -19,15 +18,149 @@ import re
19
18
 
20
19
  class UserView(FlaskView):
21
20
 
21
+ # region api
22
22
  @route('/short/')
23
+ @route('/short')
23
24
  def short_link(self):
24
- short = hiddify.add_short_link("/"+hconfig(ConfigEnum.proxy_path)+"/"+g.user.uuid+"/")
25
- ua = hiddify.get_user_agent()
26
- # if (ua['is_browser']):
27
- return f"<div style='direction:ltr'>https://{urlparse(request.base_url).hostname}/{short}/</a><br><br>"+_("This link will expire in 5 minutes")
28
- # return short
29
-
30
- # @route('/test/')
25
+ short = hiddify.add_short_link(
26
+ "/"+hconfig(ConfigEnum.proxy_path)+"/"+g.user.uuid+"/")
27
+ full_url = f"https://{urlparse(request.base_url).hostname}/{short}"
28
+ data = {
29
+ "short": f"/{short}",
30
+ "url": full_url
31
+ }
32
+ return jsonify(data)
33
+
34
+ @route('/info/')
35
+ @route('/info')
36
+ def info(self):
37
+ c = get_common_data(g.user_uuid, 'new')
38
+ data = {
39
+ 'profile_title': c['profile_title'],
40
+ 'profile_url': f"https://{urlparse(request.base_url).hostname}/{g.proxy_path}/{g.user_uuid}/#{g.user.name}",
41
+ 'profile_usage_current': g.user.current_usage_GB,
42
+ 'profile_usage_total': g.user.usage_limit_GB,
43
+ 'profile_remaining_days': g.user.remaining_days,
44
+ 'profile_reset_days': days_to_reset(g.user),
45
+ 'telegram_bot_url': f"https://t.me/{c['bot'].username}?start={g.user_uuid}" if c['bot'] else "",
46
+ 'admin_message_html': hconfig(ConfigEnum.branding_freetext),
47
+ 'admin_message_url': hconfig(ConfigEnum.branding_site),
48
+ 'brand_title': hconfig(ConfigEnum.branding_title),
49
+ 'brand_icon_url': "",
50
+ 'doh': f"https://{urlparse(request.base_url).hostname}/{g.proxy_path}/dns/dns-query"
51
+ }
52
+
53
+ return jsonify(data)
54
+
55
+ @route('/mtproxies/')
56
+ @route('/mtproxies')
57
+ def mtproxies(self):
58
+ # get domains
59
+ c = get_common_data(g.user_uuid, 'new')
60
+ mtproxies = []
61
+ # TODO: Remove duplicated domains mapped to a same ipv4 and v6
62
+ for d in c['domains']:
63
+ if d.mode not in [DomainType.direct, DomainType.relay]:
64
+ continue
65
+ hexuuid = hconfig(ConfigEnum.shared_secret, d.child_id).replace('-', '')
66
+ telegram_faketls_domain_hex = hconfig(ConfigEnum.telegram_fakedomain, d.child_id).encode('utf-8').hex()
67
+ server_link = f'tg://proxy?server={d.domain}&port=443&secret=ee{hexuuid}{telegram_faketls_domain_hex}'
68
+ mtproxies.append({'title': d.alias or d.domain, 'link': server_link})
69
+
70
+ return jsonify(mtproxies)
71
+
72
+ @route('/all-configs/')
73
+ @route('/all-configs')
74
+ def configs(self):
75
+ def create_item(name, type, domain, protocol, transport, security, link):
76
+ return {
77
+ 'name': name,
78
+ 'type': type,
79
+ 'domain': domain,
80
+ 'protocol': protocol,
81
+ 'transport': transport,
82
+ 'security': security,
83
+ 'link': link
84
+ }
85
+
86
+ items = []
87
+ base_url = f"https://{urlparse(request.base_url).hostname}/{g.proxy_path}/{g.user_uuid}/"
88
+ c = get_common_data(g.user_uuid, 'new')
89
+
90
+ # Add Auto
91
+ items.append(
92
+ create_item(
93
+ "Auto", "All", "All", "All", "All", "All",
94
+ f"{base_url}sub/?asn={c['asn']}")
95
+ )
96
+
97
+ # Add Full Singbox
98
+ items.append(
99
+ create_item(
100
+ "Full Singbox", "All", "All", "All", "All", "All",
101
+ f"{base_url}full-singbox.json?asn={c['asn']}"
102
+ )
103
+ )
104
+
105
+ # Add Clash Meta
106
+ items.append(
107
+ create_item(
108
+ "Clash Meta", "All", "All", "All", "All", "All",
109
+ 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']}"
110
+ )
111
+ )
112
+
113
+ # Add Clash
114
+ items.append(
115
+ create_item(
116
+ "Clash", "All", "All", "Except VLess", "All", "All",
117
+ 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']}"
118
+ )
119
+ )
120
+
121
+ # Add Singbox: SSh
122
+ if hconfig(ConfigEnum.ssh_server_enable):
123
+ items.append(
124
+ create_item(
125
+ "Singbox: SSH", "SSH", "SSH", "SSH", "SSH", "SSH",
126
+ f"{base_url}singbox.json?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
127
+ )
128
+ )
129
+
130
+ # Add Subscription link
131
+ items.append(
132
+ create_item(
133
+ "Subscription link", "All", "All", "All", "All", "All",
134
+ f"{base_url}all.txt?name={c['db_domain'].alias or c['db_domain'].domain}-{c['asn']}&asn={c['asn']}&mode={c['mode']}"
135
+ )
136
+ )
137
+
138
+ # Add Subscription link base64
139
+ items.append(
140
+ create_item(
141
+ "Subscription link b64", "All", "All", "All", "All", "All",
142
+ 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"
143
+ )
144
+ )
145
+
146
+ for pinfo in link_maker.get_all_validated_proxies(c['domains']):
147
+ items.append(
148
+ create_item(
149
+ pinfo["name"].replace("_", " "),
150
+ f"{'Auto ' if pinfo['dbdomain'].has_auto_ip else ''}{pinfo['mode']}",
151
+ pinfo['server'],
152
+ pinfo['proto'],
153
+ pinfo['transport'],
154
+ pinfo['l3'],
155
+ f"{link_maker.to_link(pinfo)}"
156
+ )
157
+ )
158
+
159
+ return jsonify(items)
160
+
161
+ # endregion
162
+
163
+ @route('/test/')
31
164
  def test(self):
32
165
  ua = request.user_agent.string
33
166
  if re.match('^Mozilla', ua, re.IGNORECASE):
@@ -120,7 +253,8 @@ class UserView(FlaskView):
120
253
  domain = request.args.get("domain", None)
121
254
 
122
255
  c = get_common_data(g.user_uuid, mode, filter_domain=domain)
123
- resp = Response(render_template('clash_proxies.yml', meta_or_normal=meta_or_normal, **c))
256
+ resp = Response(render_template('clash_proxies.yml',
257
+ meta_or_normal=meta_or_normal, **c))
124
258
  resp.mimetype = "text/plain"
125
259
 
126
260
  return resp
@@ -170,7 +304,8 @@ class UserView(FlaskView):
170
304
  if request.method == 'HEAD':
171
305
  resp = ""
172
306
  else:
173
- resp = render_template('clash_config.yml', typ=typ, meta_or_normal=meta_or_normal, **c, hash=hash_rnd)
307
+ resp = render_template(
308
+ 'clash_config.yml', typ=typ, meta_or_normal=meta_or_normal, **c, hash=hash_rnd)
174
309
 
175
310
  return add_headers(resp, c)
176
311
 
@@ -210,7 +345,8 @@ class UserView(FlaskView):
210
345
  if request.method == 'HEAD':
211
346
  resp = ""
212
347
  else:
213
- resp = link_maker.make_v2ray_configs(**c) # render_template('all_configs.txt', **c, base64=do_base_64)
348
+ # render_template('all_configs.txt', **c, base64=do_base_64)
349
+ resp = link_maker.make_v2ray_configs(**c)
214
350
 
215
351
  # res = ""
216
352
  # for line in resp.split("\n"):
@@ -262,7 +398,8 @@ def get_common_data(user_uuid, mode, no_domain=False, filter_domain=None):
262
398
  default_asn = request.args.get("asn")
263
399
  if filter_domain:
264
400
  domain = filter_domain
265
- db_domain = Domain.query.filter(Domain.domain == domain).first() or Domain(domain=domain, mode=DomainType.direct, cdn_ip='', show_domains=[], child_id=0)
401
+ db_domain = Domain.query.filter(Domain.domain == domain).first() or Domain(
402
+ domain=domain, mode=DomainType.direct, cdn_ip='', show_domains=[], child_id=0)
266
403
  domains = [db_domain]
267
404
  else:
268
405
  domain = urlparse(request.base_url).hostname if not no_domain else None
@@ -284,7 +421,8 @@ def get_common_data(user_uuid, mode, no_domain=False, filter_domain=None):
284
421
  domains = Domain.query.all()
285
422
  elif mode == 'new':
286
423
  # db_domain=Domain.query.filter(Domain.domain==domain).first()
287
- domains = db_domain.show_domains or Domain.query.filter(Domain.sub_link_only != True).all()
424
+ domains = db_domain.show_domains or Domain.query.filter(
425
+ Domain.sub_link_only != True).all()
288
426
  else:
289
427
 
290
428
  domains = [db_domain]
@@ -309,8 +447,10 @@ def get_common_data(user_uuid, mode, no_domain=False, filter_domain=None):
309
447
  d.has_auto_ip = False
310
448
  if d.mode == DomainType.auto_cdn_ip or d.cdn_ip:
311
449
  has_auto_cdn = True
312
- d.has_auto_ip = d.mode == DomainType.auto_cdn_ip or (d.cdn_ip and "MTN" in d.cdn_ip)
313
- d.cdn_ip = clean_ip.get_clean_ip(d.cdn_ip, d.mode == DomainType.auto_cdn_ip, default_asn)
450
+ d.has_auto_ip = d.mode == DomainType.auto_cdn_ip or (
451
+ d.cdn_ip and "MTN" in d.cdn_ip)
452
+ d.cdn_ip = clean_ip.get_clean_ip(
453
+ d.cdn_ip, d.mode == DomainType.auto_cdn_ip, default_asn)
314
454
  print("autocdn ip mode ", d.cdn_ip)
315
455
  if "*" in d.domain:
316
456
  d.domain = d.domain.replace("*", hiddify.get_random_string(5, 15))
@@ -321,9 +461,6 @@ def get_common_data(user_uuid, mode, no_domain=False, filter_domain=None):
321
461
  UserMode.monthly: 30
322
462
 
323
463
  }
324
- bot = None
325
- if hconfig(ConfigEnum.license):
326
- from hiddifypanel.panel.telegrambot import bot
327
464
 
328
465
  g.locale = hconfig(ConfigEnum.lang)
329
466
  expire_days = remaining_days(user)
@@ -333,7 +470,8 @@ def get_common_data(user_uuid, mode, no_domain=False, filter_domain=None):
333
470
  if reset_days >= expire_days:
334
471
  reset_days = 1000
335
472
  # print(reset_days,expire_days,reset_days<=expire_days)
336
- expire_s = int((datetime.date.today()+datetime.timedelta(days=expire_days)-datetime.date(1970, 1, 1)).total_seconds())
473
+ expire_s = int((datetime.date.today(
474
+ )+datetime.timedelta(days=expire_days)-datetime.date(1970, 1, 1)).total_seconds())
337
475
 
338
476
  user_ip = clean_ip.get_real_user_ip()
339
477
  asn = clean_ip.get_asn_short_name(user_ip)
@@ -360,7 +498,7 @@ def get_common_data(user_uuid, mode, no_domain=False, filter_domain=None):
360
498
  'ConfigEnum': ConfigEnum,
361
499
  'link_maker': link_maker,
362
500
  'domains': domains,
363
- "bot": bot,
501
+ "bot": g.bot,
364
502
  "db_domain": db_domain,
365
503
  "telegram_enable": hconfig(ConfigEnum.telegram_enable) and any([d for d in domains if d.mode in [DomainType.direct, DomainType.relay, DomainType.old_xtls_direct]]),
366
504
  "ip": user_ip,
@@ -375,7 +513,8 @@ def add_headers(res, c):
375
513
  resp = Response(res)
376
514
  resp.mimetype = "text/plain"
377
515
  resp.headers['Subscription-Userinfo'] = f"upload=0;download={c['usage_current_b']};total={c['usage_limit_b']};expire={c['expire_s']}"
378
- resp.headers['profile-web-page-url'] = request.base_url.rsplit('/', 1)[0].replace("http://", "https://")+"/"
516
+ resp.headers['profile-web-page-url'] = request.base_url.rsplit(
517
+ '/', 1)[0].replace("http://", "https://")+"/"
379
518
 
380
519
  if hconfig(ConfigEnum.branding_site):
381
520
  resp.headers['support-url'] = hconfig(ConfigEnum.branding_site)
@@ -119,9 +119,15 @@
119
119
  <li class="nav-item d-none">
120
120
  <a href="https://t.me/hiddify" target="_blank" class="nav-link">{{icon('brands','telegram','nav-icon')+_('admin.menu.telegram')}}</a>
121
121
  </li>
122
+ <li class="nav-item">
123
+ {% set issue_link = generate_github_issue_link_for_admin_sidebar() %}
124
+ <li class="nav-item">
125
+ <a href="{{issue_link}}" target="_blank" class=" nav-link">{{icon('solid','bug','nav-icon')+_('Bug')}}</a>
126
+ </li>
127
+ </li>
122
128
  <li class="nav-item">
123
129
  <a class="nav-item nav-link" role="button" data-toggle="modal" data-target="#modal-donation">
124
- <i class="fa-solid fa-heart nav-icon"></i>&nbsp;{{_("Donation.title")}}
130
+ {{icon('solid','bug','nav-icon')+_("Donation.title")}}
125
131
  </a>
126
132
  </li>
127
133
  </div>