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.
- hiddifypanel/VERSION +1 -1
- hiddifypanel/VERSION.py +2 -2
- hiddifypanel/hutils/__init__.py +3 -0
- hiddifypanel/hutils/crypto.py +29 -0
- hiddifypanel/hutils/encode.py +4 -0
- hiddifypanel/hutils/flask.py +4 -0
- hiddifypanel/hutils/github_issue.py +1 -1
- hiddifypanel/{models/utils.py → hutils/model.py} +14 -4
- hiddifypanel/hutils/network/net.py +46 -2
- hiddifypanel/hutils/proxy/__init__.py +4 -0
- hiddifypanel/hutils/proxy/clash.py +161 -0
- hiddifypanel/hutils/proxy/shared.py +414 -0
- hiddifypanel/hutils/proxy/singbox.py +338 -0
- hiddifypanel/hutils/proxy/xray.py +561 -0
- hiddifypanel/models/admin.py +16 -14
- hiddifypanel/models/base_account.py +4 -4
- hiddifypanel/models/config.py +1 -1
- hiddifypanel/models/proxy.py +17 -17
- hiddifypanel/models/user.py +15 -13
- hiddifypanel/panel/admin/AdminstratorAdmin.py +1 -1
- hiddifypanel/panel/admin/DomainAdmin.py +13 -27
- hiddifypanel/panel/admin/ProxyAdmin.py +3 -3
- hiddifypanel/panel/admin/SettingAdmin.py +22 -11
- hiddifypanel/panel/admin/UserAdmin.py +9 -7
- hiddifypanel/panel/cf_api.py +1 -2
- hiddifypanel/panel/commercial/ProxyDetailsAdmin.py +5 -6
- hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +1 -1
- hiddifypanel/panel/commercial/restapi/v2/user/configs_api.py +4 -7
- hiddifypanel/panel/common.py +5 -3
- hiddifypanel/panel/hiddify.py +0 -90
- hiddifypanel/panel/init_db.py +14 -9
- hiddifypanel/panel/user/__init__.py +0 -1
- hiddifypanel/panel/user/templates/all_configs copy.txt +2 -2
- hiddifypanel/panel/user/templates/all_configs.txt +2 -2
- hiddifypanel/panel/user/templates/base_singbox_config.json.j2 +2 -1
- hiddifypanel/panel/user/templates/base_xray_config.json.j2 +125 -0
- hiddifypanel/panel/user/templates/clash_config copy.yml +1 -1
- hiddifypanel/panel/user/templates/clash_config.yml +4 -4
- hiddifypanel/panel/user/templates/clash_proxies.yml +1 -1
- hiddifypanel/panel/user/templates/home/all-configs.html +2 -2
- hiddifypanel/panel/user/templates/home/all-configs_old.html +1 -1
- hiddifypanel/panel/user/templates/home/ios copy.html +2 -2
- hiddifypanel/panel/user/user.py +50 -44
- hiddifypanel/templates/admin-layout.html +16 -24
- hiddifypanel/templates/fake.html +0 -276
- hiddifypanel/templates/flaskadmin-layout.html +2 -1
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +16 -9
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +12 -6
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +9 -4
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +12 -7
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +9 -4
- hiddifypanel/translations.i18n/en.json +8 -7
- hiddifypanel/translations.i18n/fa.json +5 -4
- hiddifypanel/translations.i18n/pt.json +3 -2
- hiddifypanel/translations.i18n/ru.json +6 -5
- hiddifypanel/translations.i18n/zh.json +3 -2
- {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/METADATA +1 -1
- {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/RECORD +67 -61
- {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/WHEEL +1 -1
- hiddifypanel/panel/user/link_maker.py +0 -1089
- {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-10.10.20.dist-info → hiddifypanel-10.11.1.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
10.
|
1
|
+
10.11.1
|
hiddifypanel/VERSION.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
__version__='10.
|
1
|
+
__version__='10.11.1'
|
2
2
|
from datetime import datetime
|
3
|
-
__release_date__= datetime.strptime('2024-03-
|
3
|
+
__release_date__= datetime.strptime('2024-03-16','%Y-%m-%d')
|
hiddifypanel/hutils/__init__.py
CHANGED
@@ -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
|
hiddifypanel/hutils/encode.py
CHANGED
@@ -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:
|
hiddifypanel/hutils/flask.py
CHANGED
@@ -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
|
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,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
|