hiddifypanel 10.10.20__py3-none-any.whl → 10.11.1__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 (68) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +2 -2
  3. hiddifypanel/hutils/__init__.py +3 -0
  4. hiddifypanel/hutils/crypto.py +29 -0
  5. hiddifypanel/hutils/encode.py +4 -0
  6. hiddifypanel/hutils/flask.py +4 -0
  7. hiddifypanel/hutils/github_issue.py +1 -1
  8. hiddifypanel/{models/utils.py → hutils/model.py} +14 -4
  9. hiddifypanel/hutils/network/net.py +46 -2
  10. hiddifypanel/hutils/proxy/__init__.py +4 -0
  11. hiddifypanel/hutils/proxy/clash.py +161 -0
  12. hiddifypanel/hutils/proxy/shared.py +414 -0
  13. hiddifypanel/hutils/proxy/singbox.py +338 -0
  14. hiddifypanel/hutils/proxy/xray.py +561 -0
  15. hiddifypanel/models/admin.py +16 -14
  16. hiddifypanel/models/base_account.py +4 -4
  17. hiddifypanel/models/config.py +1 -1
  18. hiddifypanel/models/proxy.py +17 -17
  19. hiddifypanel/models/user.py +15 -13
  20. hiddifypanel/panel/admin/AdminstratorAdmin.py +1 -1
  21. hiddifypanel/panel/admin/DomainAdmin.py +13 -27
  22. hiddifypanel/panel/admin/ProxyAdmin.py +3 -3
  23. hiddifypanel/panel/admin/SettingAdmin.py +22 -11
  24. hiddifypanel/panel/admin/UserAdmin.py +9 -7
  25. hiddifypanel/panel/cf_api.py +1 -2
  26. hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +5 -6
  27. hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +1 -1
  28. hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +4 -7
  29. hiddifypanel/panel/common.py +5 -3
  30. hiddifypanel/panel/hiddify.py +0 -90
  31. hiddifypanel/panel/init_db.py +14 -9
  32. hiddifypanel/panel/user/__init__.py +0 -1
  33. hiddifypanel/panel/user/templates/all_configs copy.txt +2 -2
  34. hiddifypanel/panel/user/templates/all_configs.txt +2 -2
  35. hiddifypanel/panel/user/templates/base_singbox_config.json.j2 +2 -1
  36. hiddifypanel/panel/user/templates/base_xray_config.json.j2 +125 -0
  37. hiddifypanel/panel/user/templates/clash_config copy.yml +1 -1
  38. hiddifypanel/panel/user/templates/clash_config.yml +4 -4
  39. hiddifypanel/panel/user/templates/clash_proxies.yml +1 -1
  40. hiddifypanel/panel/user/templates/home/all-configs.html +2 -2
  41. hiddifypanel/panel/user/templates/home/all-configs_old.html +1 -1
  42. hiddifypanel/panel/user/templates/home/ios copy.html +2 -2
  43. hiddifypanel/panel/user/user.py +50 -44
  44. hiddifypanel/templates/admin-layout.html +16 -24
  45. hiddifypanel/templates/fake.html +0 -276
  46. hiddifypanel/templates/flaskadmin-layout.html +2 -1
  47. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  48. hiddifypanel/translations/en/LC_MESSAGES/messages.po +16 -9
  49. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  50. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +12 -6
  51. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  52. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +9 -4
  53. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  54. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +12 -7
  55. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  56. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +9 -4
  57. hiddifypanel/translations.i18n/en.json +8 -7
  58. hiddifypanel/translations.i18n/fa.json +5 -4
  59. hiddifypanel/translations.i18n/pt.json +3 -2
  60. hiddifypanel/translations.i18n/ru.json +6 -5
  61. hiddifypanel/translations.i18n/zh.json +3 -2
  62. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/METADATA +1 -1
  63. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/RECORD +67 -61
  64. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/WHEEL +1 -1
  65. hiddifypanel/panel/user/link_maker.py +0 -1089
  66. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/LICENSE.md +0 -0
  67. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/entry_points.txt +0 -0
  68. {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION CHANGED
@@ -1 +1 @@
1
- 10.10.20
1
+ 10.11.1
hiddifypanel/VERSION.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__='10.10.20'
1
+ __version__='10.11.1'
2
2
  from datetime import datetime
3
- __release_date__= datetime.strptime('2024-03-11','%Y-%m-%d')
3
+ __release_date__= datetime.strptime('2024-03-16','%Y-%m-%d')
@@ -8,3 +8,6 @@ from . import random
8
8
  from . import encode
9
9
  from . import auth
10
10
  from . import utils
11
+ from . import model
12
+ from . import crypto
13
+ from . import proxy
@@ -0,0 +1,29 @@
1
+ import subprocess
2
+
3
+
4
+ def get_ed25519_private_public_pair():
5
+ from cryptography.hazmat.primitives.asymmetric import ed25519
6
+ from cryptography.hazmat.primitives import serialization
7
+ privkey = ed25519.Ed25519PrivateKey.generate()
8
+ pubkey = privkey.public_key()
9
+ priv_bytes = privkey.private_bytes(
10
+ encoding=serialization.Encoding.PEM,
11
+ format=serialization.PrivateFormat.OpenSSH,
12
+ encryption_algorithm=serialization.NoEncryption(),
13
+ )
14
+ pub_bytes = pubkey.public_bytes(
15
+ encoding=serialization.Encoding.OpenSSH,
16
+ format=serialization.PublicFormat.OpenSSH,
17
+ )
18
+ return priv_bytes.decode(), pub_bytes.decode()
19
+
20
+
21
+ def get_wg_private_public_psk_pair():
22
+ try:
23
+ private_key = subprocess.run(["wg", "genkey"], capture_output=True, text=True, check=True).stdout.strip()
24
+ public_key = subprocess.run(["wg", "pubkey"], input=private_key, capture_output=True, text=True, check=True).stdout.strip()
25
+ psk = subprocess.run(["wg", "genpsk"], capture_output=True, text=True, check=True).stdout.strip()
26
+ return private_key, public_key, psk
27
+ except subprocess.CalledProcessError as e:
28
+ print(f"Error: {e}")
29
+ return None, None, None
@@ -26,6 +26,10 @@ def is_valid_uuid(val: str, version: int | None = None) -> bool:
26
26
 
27
27
  return True
28
28
 
29
+
30
+ def convert_dict_to_url(dict):
31
+ return '&' + '&'.join([f'{k}={v}' for k, v in dict.items()]) if len(dict) else ''
32
+
29
33
  # not used
30
34
  # def is_assci_alphanumeric(str):
31
35
  # for c in str:
@@ -233,6 +233,10 @@ def validate_domain_exist(form, field):
233
233
  _("Domain can not be resolved! there is a problem in your domain")) # type: ignore
234
234
 
235
235
 
236
+ def get_proxy_stats_url():
237
+ proxy_stats_url = f'{request.host_url}{g.proxy_path}/proxy-stats/'
238
+ params = f'hostname={proxy_stats_url}api/&port=443'
239
+ return f'{proxy_stats_url}?{params}/'
236
240
  # region not used
237
241
 
238
242
 
@@ -12,7 +12,6 @@ from flask import g, request, render_template
12
12
  import hiddifypanel
13
13
  from hiddifypanel.models.config import hconfig
14
14
  from hiddifypanel.models.config_enum import ConfigEnum
15
- from hiddifypanel.auth import current_account
16
15
 
17
16
 
18
17
  class __IssueUrl:
@@ -136,6 +135,7 @@ def __github_issue_details() -> dict:
136
135
 
137
136
 
138
137
  def __remove_sensetive_data_from_github_issue_link(issue_link: str):
138
+ from hiddifypanel.auth import current_account
139
139
  if current_account.uuid:
140
140
  issue_link.replace(f'{current_account.uuid}', '*******************')
141
141
 
@@ -1,6 +1,4 @@
1
-
2
-
3
- def fill_username(model) -> None:
1
+ def gen_username(model) -> None:
4
2
  from hiddifypanel import hutils
5
3
  if model.username:
6
4
  return
@@ -19,8 +17,20 @@ def fill_username(model) -> None:
19
17
  model.username += rand_str
20
18
 
21
19
 
22
- def fill_password(model) -> None:
20
+ def gen_password(model) -> None:
23
21
  from hiddifypanel import hutils
24
22
  # TODO: hash the password
25
23
  if not model.password or len(model.password) < 16:
26
24
  model.password = hutils.random.get_random_password(length=16)
25
+
26
+
27
+ def gen_wg_keys(model) -> None:
28
+ from hiddifypanel import hutils
29
+ if not model.wg_pk or not model.wg_pub or not model.wg_psk:
30
+ model.wg_pk, model.wg_pub, model.wg_psk = hutils.crypto.get_wg_private_public_psk_pair()
31
+
32
+
33
+ def gen_ed25519_keys(model) -> None:
34
+ from hiddifypanel import hutils
35
+ if not model.ed25519_private_key or not model.ed25519_public_key:
36
+ model.ed25519_private_key, model.ed25519_public_key = hutils.crypto.get_ed25519_private_public_pair()
@@ -1,7 +1,8 @@
1
- from typing import List, Literal, Union
1
+ from typing import List, Literal, Tuple, Union
2
2
  from urllib.parse import urlparse
3
3
  import urllib.request
4
4
  import ipaddress
5
+ from hiddifypanel.hutils.network.auto_ip_selector import IPASN
5
6
  import netifaces
6
7
  import requests
7
8
  import random
@@ -16,7 +17,7 @@ from hiddifypanel.models import *
16
17
  from hiddifypanel.cache import cache
17
18
 
18
19
 
19
- def get_domain_ip(domain: str, retry: int = 3, version: Literal[4, 6] = None) -> Union[ipaddress.IPv4Address, ipaddress.IPv6Address, None]:
20
+ def get_domain_ip(domain: str, retry: int = 3, version: Literal[4, 6] | None = None) -> Union[ipaddress.IPv4Address, ipaddress.IPv6Address, None]:
20
21
  res = None
21
22
  if not version:
22
23
  try:
@@ -326,3 +327,46 @@ def add_number_to_ipv6(ip: str, number: int) -> str:
326
327
  modified_ipv6 = ":".join(segments)
327
328
 
328
329
  return modified_ipv6
330
+
331
+
332
+ def is_in_same_asn(domain_or_ip: str, domain_or_ip_target: str) -> bool:
333
+ '''Returns True if domain is in panel ASN'''
334
+ if not IPASN:
335
+ return False
336
+ try:
337
+ ip = domain_or_ip if is_ip(domain_or_ip) else get_domain_ip(domain_or_ip)
338
+ ip_target = domain_or_ip_target if is_ip(domain_or_ip_target) else get_domain_ip(domain_or_ip_target)
339
+
340
+ if not ip or not ip_target:
341
+ return False
342
+
343
+ ip_asn = get_ip_asn_name(ip)
344
+ ip_target_asn = get_ip_asn_name(ip_target)
345
+
346
+ if not ip_asn or not ip_target_asn:
347
+ return False
348
+
349
+ return ip_asn == ip_target_asn
350
+ except Exception as e:
351
+ print(f"An error occurred: {e}")
352
+ return False
353
+
354
+ # hutils.flask.flash(_("selected domain for REALITY is not in the same ASN. To better use of the protocol, it is better to find a domain in the same ASN.") +
355
+ # f"<br> Server ASN={asn_ipv4.get('autonomous_system_organization','unknown')}<br>{domain}_ASN={asn_dip.get('autonomous_system_organization','unknown')}", "warning")
356
+
357
+
358
+ def get_ip_asn_name(ip: ipaddress.IPv4Address | ipaddress.IPv6Address | str) -> str:
359
+ try:
360
+ if asn := IPASN.get(str(ip)):
361
+ return str(asn.get('autonomous_system_organization', ''))
362
+ return ''
363
+ except:
364
+ return ''
365
+
366
+
367
+ def is_ip(input: str):
368
+ try:
369
+ _ = ipaddress.ip_address(input)
370
+ return True
371
+ except:
372
+ return False
@@ -0,0 +1,4 @@
1
+ from .shared import *
2
+ from . import xray
3
+ from . import singbox
4
+ from . import clash
@@ -0,0 +1,161 @@
1
+ import yaml
2
+ from hiddifypanel.models import Proxy, ProxyCDN, ProxyL3, ProxyProto, ProxyTransport, Domain
3
+ from hiddifypanel import hutils
4
+
5
+
6
+ def get_clash_config_names(meta_or_normal, domains: list[Domain]):
7
+ allp = []
8
+ for pinfo in hutils.proxy.get_valid_proxies(domains):
9
+ clash = to_clash(pinfo, meta_or_normal)
10
+ if 'msg' not in clash:
11
+ allp.append(clash['name'])
12
+
13
+ return yaml.dump(allp, sort_keys=False)
14
+
15
+
16
+ def get_all_clash_configs(meta_or_normal, domains: list[Domain]):
17
+ allp = []
18
+ for pinfo in hutils.proxy.get_valid_proxies(domains):
19
+ clash = to_clash(pinfo, meta_or_normal)
20
+ if 'msg' not in clash:
21
+ allp.append(clash)
22
+
23
+ return yaml.dump({"proxies": allp}, sort_keys=False)
24
+
25
+ # def to_clash_yml(proxy):
26
+ # return yaml.dump(to_clash(proxy,'normal'))
27
+
28
+
29
+ def to_clash(proxy, meta_or_normal):
30
+
31
+ name = proxy['name']
32
+ if proxy['l3'] == "kcp":
33
+ return {'name': name, 'msg': "clash does not support kcp", 'type': 'debug'}
34
+ if proxy['proto'] == "ssh":
35
+ return {'name': name, 'msg': "clash does not support ssh", 'type': 'debug'}
36
+ if meta_or_normal == "normal":
37
+ if proxy['proto'] in ["vless", 'tuic', 'hysteria2']:
38
+ return {'name': name, 'msg': f"{proxy['proto']} not supported in clash", 'type': 'debug'}
39
+ if proxy.get('flow'):
40
+ return {'name': name, 'msg': "xtls not supported in clash", 'type': 'debug'}
41
+ if proxy['transport'] == "shadowtls":
42
+ return {'name': name, 'msg': "shadowtls not supported in clash", 'type': 'debug'}
43
+ if proxy['l3'] == ProxyL3.tls_h2 and proxy['proto'] in [ProxyProto.vmess, ProxyProto.vless] and proxy['dbe'].cdn == ProxyCDN.direct:
44
+ return {'name': name, 'msg': "bug tls_h2 vmess and vless in clash meta", 'type': 'warning'}
45
+ base = {}
46
+ # vmess ws
47
+ base["name"] = f"""{proxy['extra_info']} {proxy["name"]} § {proxy['port']} {proxy["dbdomain"].id}"""
48
+ base["type"] = str(proxy["proto"])
49
+ base["server"] = proxy["server"]
50
+ base["port"] = proxy["port"]
51
+ base['alpn'] = proxy['alpn'].split(',')
52
+ if proxy["proto"] == "ssr":
53
+ base["cipher"] = proxy["cipher"]
54
+ base["password"] = proxy["uuid"]
55
+ base["udp"] = True
56
+ base["obfs"] = proxy["ssr-obfs"]
57
+ base["protocol"] = proxy["ssr-protocol"]
58
+ base["obfs-param"] = proxy["fakedomain"]
59
+ return base
60
+ elif proxy["proto"] == "tuic":
61
+ base["uuid"] = proxy["uuid"]
62
+ base["password"] = proxy["uuid"]
63
+ base["disable-sni"] = proxy['allow_insecure']
64
+ base["reduce-rtt"] = True
65
+ base["request-timeout"] = 8000
66
+ base["udp-relay-mode"] = 'native'
67
+ base["congestion-controller"] = 'cubic'
68
+ base['sni'] = proxy['sni']
69
+ return base
70
+ elif proxy["proto"] in ["ss", "v2ray"]:
71
+ base["cipher"] = proxy["cipher"]
72
+ base["password"] = proxy["password"]
73
+ base["udp_over_tcp"] = True
74
+ if proxy["transport"] == "faketls":
75
+ base["plugin"] = "obfs"
76
+ base["plugin-opts"] = {
77
+ "mode": 'tls',
78
+ "host": proxy["fakedomain"]
79
+ }
80
+ elif proxy["transport"] == "shadowtls":
81
+ base["plugin"] = "shadow-tls"
82
+ base["plugin-opts"] = {
83
+ "host": proxy["fakedomain"],
84
+ "password": proxy["proxy_path"],
85
+ "version": 3 # support 1/2/3
86
+
87
+ }
88
+
89
+ elif proxy["proto"] == "v2ray":
90
+ base["plugin"] = "v2ray-plugin"
91
+ base["type"] = "ss"
92
+ base["plugin-opts"] = {
93
+ "mode": "websocket",
94
+ "tls": "tls" in proxy["l3"],
95
+ "skip-cert-verify": proxy["mode"] == "Fake" or proxy['allow_insecure'],
96
+ "host": proxy['sni'],
97
+ "path": proxy["path"]
98
+ }
99
+ return base
100
+ elif proxy["proto"] == "trojan":
101
+ base["password"] = proxy["uuid"]
102
+ base["sni"] = proxy["sni"]
103
+
104
+ else:
105
+ base["uuid"] = proxy["uuid"]
106
+ base["servername"] = proxy["sni"]
107
+ base["tls"] = "tls" in proxy["l3"] or "reality" in proxy["l3"]
108
+ if meta_or_normal == "meta":
109
+ base['client-fingerprint'] = proxy['fingerprint']
110
+ if proxy.get('flow'):
111
+ base["flow"] = proxy['flow']
112
+ # base["flow-show"] = True
113
+
114
+ if proxy["proto"] == "vmess":
115
+ base["alterId"] = 0
116
+ base["cipher"] = proxy["cipher"]
117
+ base["udp"] = True
118
+
119
+ base["skip-cert-verify"] = proxy["mode"] == "Fake"
120
+
121
+ base["network"] = proxy["transport"]
122
+
123
+ if base["network"] == "ws":
124
+ base["ws-opts"] = {
125
+ "path": proxy["path"]
126
+ }
127
+ if "host" in proxy:
128
+ base["ws-opts"]["headers"] = {"Host": proxy["host"]}
129
+
130
+ if base["network"] == "tcp" and proxy['alpn'] != 'h2':
131
+ if proxy['transport'] != ProxyTransport.XTLS:
132
+ base["network"] = "http"
133
+
134
+ if "path" in proxy:
135
+ base["http-opts"] = {
136
+ "path": [proxy["path"]]
137
+ }
138
+ if 'host' in proxy:
139
+ base["http-opts"]["host"] = [proxy["host"]]
140
+ if base["network"] == "tcp" and proxy['alpn'] == 'h2':
141
+ base["network"] = "h2"
142
+
143
+ if "path" in proxy:
144
+ base["h2-opts"] = {
145
+ "path": proxy["path"]
146
+ }
147
+ if 'host' in proxy:
148
+ base["h2-opts"]["host"] = [proxy["host"]]
149
+ if base["network"] == "grpc":
150
+ base["grpc-opts"] = {
151
+ "grpc-service-name": proxy["grpc_service_name"]
152
+ }
153
+ if proxy['l3'] == ProxyL3.reality:
154
+ base["reality-opts"] = {
155
+ "public-key": proxy['reality_pbk'],
156
+ "short-id": proxy['reality_short_id'],
157
+ }
158
+ if proxy["transport"] != 'grpc':
159
+ base["network"] = 'tcp'
160
+
161
+ return base