hiddifypanel 7.1.9__py3-none-any.whl → 8.0.0.dev0__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 +18 -15
- hiddifypanel/cache.py +11 -0
- hiddifypanel/drivers/abstract_driver.py +5 -0
- hiddifypanel/drivers/singbox_api.py +52 -0
- hiddifypanel/drivers/ssh_liberty_bridge_api.py +53 -0
- hiddifypanel/drivers/user_driver.py +30 -0
- hiddifypanel/drivers/xray_api.py +113 -0
- hiddifypanel/models/config.py +3 -1
- hiddifypanel/models/config_enum.py +7 -1
- hiddifypanel/models/proxy.py +3 -0
- hiddifypanel/models/user.py +3 -0
- hiddifypanel/panel/admin/UserAdmin.py +6 -6
- hiddifypanel/panel/commercial/restapi/resources.py +2 -2
- hiddifypanel/panel/custom_widgets.py +10 -10
- hiddifypanel/panel/hiddify.py +23 -1
- hiddifypanel/panel/init_db.py +27 -1
- hiddifypanel/panel/usage.py +6 -17
- hiddifypanel/panel/user/link_maker.py +1 -1
- hiddifypanel/panel/user/templates/home/all-configs.html +104 -91
- hiddifypanel/panel/user/templates/singbox_config.json +110 -0
- hiddifypanel/panel/user/user.py +32 -26
- hiddifypanel/templates/fake.html +4 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +72 -18
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +62 -18
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +62 -18
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +62 -18
- {hiddifypanel-7.1.9.dist-info → hiddifypanel-8.0.0.dev0.dist-info}/METADATA +3 -1
- {hiddifypanel-7.1.9.dist-info → hiddifypanel-8.0.0.dev0.dist-info}/RECORD +38 -33
- hiddifypanel/singbox_api.py +0 -55
- hiddifypanel/xray_api.py +0 -115
- {hiddifypanel-7.1.9.dist-info → hiddifypanel-8.0.0.dev0.dist-info}/LICENSE.md +0 -0
- {hiddifypanel-7.1.9.dist-info → hiddifypanel-8.0.0.dev0.dist-info}/WHEEL +0 -0
- {hiddifypanel-7.1.9.dist-info → hiddifypanel-8.0.0.dev0.dist-info}/entry_points.txt +0 -0
- {hiddifypanel-7.1.9.dist-info → hiddifypanel-8.0.0.dev0.dist-info}/top_level.txt +0 -0
hiddifypanel/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
8.0.0.dev
|
hiddifypanel/VERSION.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
__version__='
|
1
|
+
__version__='8.0.0.dev'
|
2
2
|
from datetime import datetime
|
3
|
-
__release_date__= datetime.strptime('2023-
|
3
|
+
__release_date__= datetime.strptime('2023-08-03','%Y-%m-%d')
|
hiddifypanel/base.py
CHANGED
@@ -5,30 +5,29 @@ from flask import Flask, request, g
|
|
5
5
|
from flask_babelex import Babel
|
6
6
|
from hiddifypanel.panel.init_db import init_db
|
7
7
|
import hiddifypanel
|
8
|
-
from hiddifypanel.models import
|
8
|
+
from hiddifypanel.models import *
|
9
9
|
from dotenv import dotenv_values
|
10
10
|
import os
|
11
|
-
|
11
|
+
|
12
|
+
|
13
|
+
def create_app(cli=False, **config):
|
12
14
|
app = Flask(__name__, static_url_path="/<proxy_path>/static/", instance_relative_config=True)
|
13
15
|
|
14
|
-
for c,v in dotenv_values('app.cfg').items():
|
16
|
+
for c, v in dotenv_values('app.cfg').items():
|
15
17
|
if v.isdecimal():
|
16
|
-
v= int(v)
|
18
|
+
v = int(v)
|
17
19
|
else:
|
18
|
-
v=True if v.lower()=="true" else (False if v.lower()=="false" else v)
|
20
|
+
v = True if v.lower() == "true" else (False if v.lower() == "false" else v)
|
19
21
|
|
20
|
-
|
21
|
-
app.config[c]=v
|
22
|
-
|
22
|
+
app.config[c] = v
|
23
23
|
|
24
24
|
app.jinja_env.line_statement_prefix = '%'
|
25
|
-
app.is_cli=cli
|
25
|
+
app.is_cli = cli
|
26
26
|
flask_bootstrap.Bootstrap4(app)
|
27
|
-
|
27
|
+
|
28
28
|
hiddifypanel.panel.database.init_app(app)
|
29
29
|
with app.app_context():
|
30
30
|
init_db()
|
31
|
-
|
32
31
|
|
33
32
|
hiddifypanel.panel.common.init_app(app)
|
34
33
|
hiddifypanel.panel.admin.init_app(app)
|
@@ -58,10 +57,14 @@ def create_app(cli=False,**config):
|
|
58
57
|
|
59
58
|
@app.before_request
|
60
59
|
def check_csrf():
|
61
|
-
if "/admin/user/" in request.base_url:
|
62
|
-
|
63
|
-
if "/admin/
|
64
|
-
|
60
|
+
if "/admin/user/" in request.base_url:
|
61
|
+
return
|
62
|
+
if "/admin/domain/" in request.base_url:
|
63
|
+
return
|
64
|
+
if "/admin/actions/" in request.base_url:
|
65
|
+
return
|
66
|
+
if "/api/v1/" in request.base_url:
|
67
|
+
return
|
65
68
|
csrf.protect()
|
66
69
|
|
67
70
|
app.jinja_env.globals['get_locale'] = get_locale
|
hiddifypanel/cache.py
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
from redis_cache import RedisCache
|
2
|
+
import redis
|
3
|
+
|
4
|
+
redis_client = redis.from_url('unix:///opt/hiddify-config/other/redis/run.sock?db=0', decode_responses=True)
|
5
|
+
|
6
|
+
|
7
|
+
def exception_handler(**kwargs):
|
8
|
+
pass
|
9
|
+
|
10
|
+
|
11
|
+
cache = RedisCache(redis_client=redis_client, exception_handler=exception_handler)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import xtlsapi
|
2
|
+
from hiddifypanel.models import *
|
3
|
+
from .abstract_driver import DriverABS
|
4
|
+
|
5
|
+
|
6
|
+
class SingboxApi(DriverABS):
|
7
|
+
def get_singbox_client(self):
|
8
|
+
if hconfig(ConfigEnum.is_parent):
|
9
|
+
return
|
10
|
+
return xtlsapi.SingboxClient('127.0.0.1', 10086)
|
11
|
+
|
12
|
+
def get_enabled_users(self):
|
13
|
+
if hconfig(ConfigEnum.is_parent):
|
14
|
+
return
|
15
|
+
|
16
|
+
def get_inbound_tags():
|
17
|
+
if hconfig(ConfigEnum.is_parent):
|
18
|
+
return
|
19
|
+
try:
|
20
|
+
xray_client = self.get_singbox_client()
|
21
|
+
inbounds = [inb.name.split(">>>")[1] for inb in xray_client.stats_query('inbound')]
|
22
|
+
print(f"Success in get inbound tags {inbounds}")
|
23
|
+
except Exception as e:
|
24
|
+
print(f"error in get inbound tags {e}")
|
25
|
+
inbounds = []
|
26
|
+
return list(set(inbounds))
|
27
|
+
|
28
|
+
def add_client(user):
|
29
|
+
if hconfig(ConfigEnum.is_parent):
|
30
|
+
return
|
31
|
+
# raise NotImplementedError()
|
32
|
+
|
33
|
+
def remove_client(user):
|
34
|
+
if hconfig(ConfigEnum.is_parent):
|
35
|
+
return
|
36
|
+
# raise NotImplementedError()
|
37
|
+
|
38
|
+
def get_usage(uuid, reset=False):
|
39
|
+
if hconfig(ConfigEnum.is_parent):
|
40
|
+
return
|
41
|
+
xray_client = self.get_singbox_client()
|
42
|
+
d = xray_client.get_client_download_traffic(f'{uuid}@hiddify.com', reset=reset)
|
43
|
+
u = xray_client.get_client_upload_traffic(f'{uuid}@hiddify.com', reset=reset)
|
44
|
+
print(f"Success {uuid} d={d} u={u}")
|
45
|
+
res = None
|
46
|
+
if d is None:
|
47
|
+
res = u
|
48
|
+
elif u is None:
|
49
|
+
res = d
|
50
|
+
else:
|
51
|
+
res = d + u
|
52
|
+
return res
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from .abstract_driver import DriverABS
|
2
|
+
from hiddifypanel.models import *
|
3
|
+
import redis
|
4
|
+
|
5
|
+
USERS_SET = "ssh-server:users"
|
6
|
+
USERS_USAGE = "ssh-server:users-usage"
|
7
|
+
|
8
|
+
|
9
|
+
class SSHLibertyBridgeApi(DriverABS):
|
10
|
+
|
11
|
+
def get_ssh_redis_client(self):
|
12
|
+
if not getattr(self, 'redis_client'):
|
13
|
+
self.redis_client = redis.from_url(hconfig(ConfigEnum.ssh_server_redis_url), decode_responses=True)
|
14
|
+
|
15
|
+
return self.redis_client
|
16
|
+
|
17
|
+
def get_enabled_users(self):
|
18
|
+
if hconfig(ConfigEnum.is_parent):
|
19
|
+
return
|
20
|
+
redis_client = self.get_ssh_redis_client()
|
21
|
+
members = redis_client.smembers(USERS_SET)
|
22
|
+
return [m.split("::")[0] for m in members]
|
23
|
+
|
24
|
+
def add_client(self, user):
|
25
|
+
if hconfig(ConfigEnum.is_parent):
|
26
|
+
return
|
27
|
+
redis_client = self.get_ssh_redis_client()
|
28
|
+
redis_client.sadd(USERS_SET, f'{user.uuid}::{user.ed25519_public_key}')
|
29
|
+
redis_client.save()
|
30
|
+
|
31
|
+
def remove_client(self, user):
|
32
|
+
if hconfig(ConfigEnum.is_parent):
|
33
|
+
return
|
34
|
+
redis_client = self.get_ssh_redis_client()
|
35
|
+
redis_client.srem(USERS_SET, f'{user.uuid}::{user.ed25519_public_key}')
|
36
|
+
redis_client.srem(USERS_USAGE, user.uuid)
|
37
|
+
redis_client.save()
|
38
|
+
|
39
|
+
def get_usage(self, client_uuid: str, reset: bool = True) -> int:
|
40
|
+
if hconfig(ConfigEnum.is_parent):
|
41
|
+
return
|
42
|
+
value = redis_client.hget(USERS_USAGE, client_uuid)
|
43
|
+
|
44
|
+
if value is None:
|
45
|
+
return 0
|
46
|
+
|
47
|
+
value = int(value)
|
48
|
+
|
49
|
+
if reset:
|
50
|
+
redis_client.hincrby(USERS_USAGE, client_uuid, -value)
|
51
|
+
redis_client.save()
|
52
|
+
|
53
|
+
return value
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
from .ssh_liberty_bridge_api import SSHLibertyBridgeApi
|
4
|
+
from .xray_api import XrayApi
|
5
|
+
from .singbox_api import SingboxApi
|
6
|
+
|
7
|
+
|
8
|
+
drivers = [XrayApi(), SingboxApi(), SSHLibertyBridgeApi()]
|
9
|
+
|
10
|
+
|
11
|
+
def get_users_usage(reset=True):
|
12
|
+
res = {}
|
13
|
+
for user in User.query.all():
|
14
|
+
d = 0
|
15
|
+
for driver in drivers:
|
16
|
+
d += driver.get_usage(user.uuid, reset=True) or 0
|
17
|
+
res[user] = {'usage': d, 'ips': ''}
|
18
|
+
return res
|
19
|
+
|
20
|
+
|
21
|
+
def get_enabled_users(self):
|
22
|
+
d = []
|
23
|
+
for driver in drivers:
|
24
|
+
d += driver.get_enabled_users() or []
|
25
|
+
return d
|
26
|
+
|
27
|
+
|
28
|
+
def add_client(self, user):
|
29
|
+
for driver in drivers:
|
30
|
+
d += driver.add_client(user)
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import xtlsapi
|
2
|
+
from hiddifypanel.models import *
|
3
|
+
from .abstract_driver import DriverABS
|
4
|
+
|
5
|
+
|
6
|
+
class XrayApi(DriverABS):
|
7
|
+
def get_xray_client(self):
|
8
|
+
if hconfig(ConfigEnum.is_parent):
|
9
|
+
return
|
10
|
+
return xtlsapi.XrayClient('127.0.0.1', 10085)
|
11
|
+
|
12
|
+
def get_enabled_users(self):
|
13
|
+
if hconfig(ConfigEnum.is_parent):
|
14
|
+
return
|
15
|
+
xray_client = self.get_xray_client()
|
16
|
+
users = User.query.all()
|
17
|
+
t = "xtls"
|
18
|
+
protocol = "vless"
|
19
|
+
enabled = {}
|
20
|
+
for u in users:
|
21
|
+
uuid = u.uuid
|
22
|
+
try:
|
23
|
+
xray_client.add_client(t, f'{uuid}', f'{uuid}@hiddify.com', protocol=protocol, flow='xtls-rprx-vision', alter_id=0, cipher='chacha20_poly1305')
|
24
|
+
xray_client.remove_client(t, f'{uuid}@hiddify.com')
|
25
|
+
enabled[uuid] = 0
|
26
|
+
except xtlsapi.exceptions.EmailAlreadyExists as e:
|
27
|
+
enabled[uuid] = 1
|
28
|
+
except Exception as e:
|
29
|
+
print(f"error {e}")
|
30
|
+
enabled[uuid] = e
|
31
|
+
return enabled
|
32
|
+
|
33
|
+
def get_inbound_tags(self):
|
34
|
+
if hconfig(ConfigEnum.is_parent):
|
35
|
+
return
|
36
|
+
try:
|
37
|
+
xray_client = self.get_xray_client()
|
38
|
+
inbounds = [inb.name.split(">>>")[1] for inb in xray_client.stats_query('inbound')]
|
39
|
+
print(f"Success in get inbound tags {inbounds}")
|
40
|
+
except Exception as e:
|
41
|
+
print(f"error in get inbound tags {e}")
|
42
|
+
inbounds = []
|
43
|
+
return list(set(inbounds))
|
44
|
+
|
45
|
+
def add_client(self, user):
|
46
|
+
if hconfig(ConfigEnum.is_parent):
|
47
|
+
return
|
48
|
+
uuid = user.uuid
|
49
|
+
xray_client = self.get_xray_client()
|
50
|
+
tags = self.get_inbound_tags()
|
51
|
+
proto_map = {
|
52
|
+
'vless': 'vless',
|
53
|
+
'realityin': 'vless',
|
54
|
+
'xtls': 'vless',
|
55
|
+
'quic': 'vless',
|
56
|
+
'trojan': 'trojan',
|
57
|
+
'vmess': 'vmess',
|
58
|
+
'ss': 'shadowsocks',
|
59
|
+
'v2ray': 'shadowsocks',
|
60
|
+
'kcp': 'vless',
|
61
|
+
'dispatcher': 'trojan',
|
62
|
+
}
|
63
|
+
|
64
|
+
def proto(t):
|
65
|
+
res = '', ''
|
66
|
+
for p, protocol in proto_map.items():
|
67
|
+
if p in t:
|
68
|
+
res = p, protocol
|
69
|
+
break
|
70
|
+
return res
|
71
|
+
for t in tags:
|
72
|
+
try:
|
73
|
+
p, protocol = proto(t)
|
74
|
+
if not p:
|
75
|
+
continue
|
76
|
+
if protocol == "vless" and p != "xtls" and p != "realityin":
|
77
|
+
xray_client.add_client(t, f'{uuid}', f'{uuid}@hiddify.com', protocol=protocol, flow='\0',)
|
78
|
+
else:
|
79
|
+
xray_client.add_client(t, f'{uuid}', f'{uuid}@hiddify.com', protocol=protocol, flow='xtls-rprx-vision', alter_id=0, cipher='chacha20_poly1305')
|
80
|
+
print(f"Success add {uuid} {t}")
|
81
|
+
except Exception as e:
|
82
|
+
print(f"error in add {uuid} {t} {e}")
|
83
|
+
pass
|
84
|
+
|
85
|
+
def remove_client(self, uuid):
|
86
|
+
if hconfig(ConfigEnum.is_parent):
|
87
|
+
return
|
88
|
+
xray_client = self.get_xray_client()
|
89
|
+
tags = self.get_inbound_tags()
|
90
|
+
|
91
|
+
for t in tags:
|
92
|
+
try:
|
93
|
+
xray_client.remove_client(t, f'{uuid}@hiddify.com')
|
94
|
+
print(f"Success remove {uuid} {t}")
|
95
|
+
except Exception as e:
|
96
|
+
print(f"error in remove {uuid} {t} {e}")
|
97
|
+
pass
|
98
|
+
|
99
|
+
def get_usage(self, uuid, reset=False):
|
100
|
+
if hconfig(ConfigEnum.is_parent):
|
101
|
+
return
|
102
|
+
xray_client = self.get_xray_client()
|
103
|
+
d = xray_client.get_client_download_traffic(f'{uuid}@hiddify.com', reset=reset)
|
104
|
+
u = xray_client.get_client_upload_traffic(f'{uuid}@hiddify.com', reset=reset)
|
105
|
+
print(f"Success {uuid} d={d} u={u}")
|
106
|
+
res = None
|
107
|
+
if d is None:
|
108
|
+
res = u
|
109
|
+
elif u is None:
|
110
|
+
res = d
|
111
|
+
else:
|
112
|
+
res = d + u
|
113
|
+
return res
|
hiddifypanel/models/config.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# from hiddifypanel.cache import cache
|
1
2
|
from sqlalchemy_serializer import SerializerMixin
|
2
3
|
from flask import Flask
|
3
4
|
from flask_sqlalchemy import SQLAlchemy
|
@@ -48,6 +49,7 @@ class StrConfig(db.Model, SerializerMixin):
|
|
48
49
|
}
|
49
50
|
|
50
51
|
|
52
|
+
# @cache.cache()
|
51
53
|
def hconfig(key: ConfigEnum, child_id=0):
|
52
54
|
value = None
|
53
55
|
try:
|
@@ -74,7 +76,7 @@ def hconfig(key: ConfigEnum, child_id=0):
|
|
74
76
|
|
75
77
|
|
76
78
|
def set_hconfig(key: ConfigEnum, value, child_id=0, commit=True):
|
77
|
-
|
79
|
+
hconfig.invalidate(key, child_id)
|
78
80
|
if key.type() == bool:
|
79
81
|
dbconf = BoolConfig.query.filter(BoolConfig.key == key, BoolConfig.child_id == child_id).first()
|
80
82
|
if not dbconf:
|
@@ -26,7 +26,7 @@ class ConfigCategory(StrEnum):
|
|
26
26
|
telegram = auto()
|
27
27
|
http = auto()
|
28
28
|
tls = auto()
|
29
|
-
|
29
|
+
ssh = auto()
|
30
30
|
ssfaketls = auto()
|
31
31
|
shadowtls = auto()
|
32
32
|
restls = auto()
|
@@ -41,6 +41,9 @@ class ConfigCategory(StrEnum):
|
|
41
41
|
|
42
42
|
|
43
43
|
class ConfigEnum(StrEnum):
|
44
|
+
ssh_server_redis_url = auto()
|
45
|
+
ssh_server_ports = auto()
|
46
|
+
ssh_server_enable = auto()
|
44
47
|
first_setup = auto()
|
45
48
|
core_type = auto()
|
46
49
|
warp_enable = auto()
|
@@ -139,6 +142,9 @@ class ConfigEnum(StrEnum):
|
|
139
142
|
|
140
143
|
def info(self):
|
141
144
|
map = {
|
145
|
+
self.ssh_server_redis_url: {'category': ConfigCategory.hidden},
|
146
|
+
self.ssh_server_ports: {'category': ConfigCategory.ssh},
|
147
|
+
self.ssh_server_enable: {'category': ConfigCategory.ssh},
|
142
148
|
self.core_type: {'category': ConfigCategory.advanced, 'apply_mode': 'apply'},
|
143
149
|
self.dns_server: {'category': ConfigCategory.general, 'apply_mode': 'apply'},
|
144
150
|
self.warp_enable: {'category': ConfigCategory.hidden, 'type': bool, 'apply_mode': 'restart'},
|
hiddifypanel/models/proxy.py
CHANGED
@@ -25,6 +25,7 @@ class ProxyTransport(StrEnum):
|
|
25
25
|
# h1=auto()
|
26
26
|
WS = auto()
|
27
27
|
tcp = auto()
|
28
|
+
ssh = auto()
|
28
29
|
|
29
30
|
|
30
31
|
class ProxyCDN(StrEnum):
|
@@ -40,6 +41,7 @@ class ProxyProto(StrEnum):
|
|
40
41
|
ss = auto()
|
41
42
|
v2ray = auto()
|
42
43
|
ssr = auto()
|
44
|
+
ssh = auto()
|
43
45
|
|
44
46
|
|
45
47
|
class ProxyL3(StrEnum):
|
@@ -49,6 +51,7 @@ class ProxyL3(StrEnum):
|
|
49
51
|
reality = auto()
|
50
52
|
http = auto()
|
51
53
|
kcp = auto()
|
54
|
+
ssh = auto()
|
52
55
|
# hysteria=auto()
|
53
56
|
|
54
57
|
|
hiddifypanel/models/user.py
CHANGED
@@ -61,6 +61,9 @@ class User(db.Model, SerializerMixin):
|
|
61
61
|
max_ips = db.Column(db.Integer, default=1000, nullable=False)
|
62
62
|
details = db.relationship('UserDetail', cascade="all,delete", backref='user', lazy='dynamic',)
|
63
63
|
enable = db.Column(db.Boolean, default=True, nullable=False)
|
64
|
+
ed25519_private_key=db.Column(db.String(100))
|
65
|
+
ed25519_public_key=db.Column(db.String(100))
|
66
|
+
|
64
67
|
|
65
68
|
@property
|
66
69
|
def remaining_days(self):
|
@@ -6,7 +6,7 @@ from flask import Markup, g, request, url_for, abort
|
|
6
6
|
from wtforms.validators import Regexp, ValidationError
|
7
7
|
import re
|
8
8
|
import uuid
|
9
|
-
from hiddifypanel import
|
9
|
+
from hiddifypanel.drivers import user_driver
|
10
10
|
from .adminlte import AdminLTEModelView
|
11
11
|
# from gettext import gettext as _
|
12
12
|
from flask_babelex import gettext as __
|
@@ -195,7 +195,7 @@ class UserAdmin(AdminLTEModelView):
|
|
195
195
|
def on_model_delete(self, model):
|
196
196
|
if len(User.query.all()) <= 1:
|
197
197
|
raise ValidationError(f"at least one user should exist")
|
198
|
-
|
198
|
+
user_driver.remove_client(model.uuid)
|
199
199
|
# hiddify.flash_config_success()
|
200
200
|
|
201
201
|
# def is_accessible(self):
|
@@ -251,7 +251,7 @@ class UserAdmin(AdminLTEModelView):
|
|
251
251
|
raise ValidationError(_('You have too much users! You can have only %(active)s active users and %(total)s users',
|
252
252
|
active=g.admin.max_active_users, total=g.admin.max_users))
|
253
253
|
if old_user and old_user.uuid != model.uuid:
|
254
|
-
|
254
|
+
user_driver.remove_client(old_user.uuid)
|
255
255
|
|
256
256
|
# model.expiry_time=datetime.date.today()+datetime.timedelta(days=model.expiry_time)
|
257
257
|
|
@@ -267,13 +267,13 @@ class UserAdmin(AdminLTEModelView):
|
|
267
267
|
|
268
268
|
user = User.query.filter(User.uuid == model.uuid).first()
|
269
269
|
if is_user_active(user):
|
270
|
-
|
270
|
+
user_driver.add_client(model.uuid)
|
271
271
|
else:
|
272
|
-
|
272
|
+
user_driver.remove_client(model.uuid)
|
273
273
|
hiddify.quick_apply_users()
|
274
274
|
|
275
275
|
def after_model_delete(self, model):
|
276
|
-
|
276
|
+
user_driver.remove_client(model.uuid)
|
277
277
|
|
278
278
|
hiddify.quick_apply_users()
|
279
279
|
|
@@ -5,7 +5,7 @@ import datetime
|
|
5
5
|
from hiddifypanel.models import *
|
6
6
|
from urllib.parse import urlparse
|
7
7
|
from hiddifypanel.panel import hiddify
|
8
|
-
from hiddifypanel import
|
8
|
+
from hiddifypanel.drivers import user_driver
|
9
9
|
# class AllResource(Resource):
|
10
10
|
# def get(self):
|
11
11
|
# return jsonify(
|
@@ -27,7 +27,7 @@ class UserResource(Resource):
|
|
27
27
|
def post(self):
|
28
28
|
data = request.json
|
29
29
|
hiddify.add_or_update_user(**data)
|
30
|
-
|
30
|
+
user_driver.add_client(data['uuid'])
|
31
31
|
hiddify.quick_apply_users()
|
32
32
|
|
33
33
|
return jsonify({'status': 200, 'msg': 'ok'})
|
@@ -1,3 +1,12 @@
|
|
1
|
+
from flask_admin.contrib.sqla import ModelView
|
2
|
+
from wtforms.widgets import TextArea
|
3
|
+
from wtforms import TextAreaField
|
4
|
+
from flask_bootstrap import SwitchField
|
5
|
+
from wtforms.fields import StringField, IntegerField, SelectField
|
6
|
+
from hiddifypanel.panel.hiddify import flash
|
7
|
+
from hiddifypanel.panel import hiddify
|
8
|
+
from flask_babelex import lazy_gettext as _
|
9
|
+
from flask_babelex import gettext as __
|
1
10
|
from flask_admin.contrib import sqla
|
2
11
|
from hiddifypanel.panel.database import db
|
3
12
|
import datetime
|
@@ -6,17 +15,8 @@ from flask import Markup, g
|
|
6
15
|
from wtforms.validators import Regexp, ValidationError
|
7
16
|
import re
|
8
17
|
import uuid
|
9
|
-
|
18
|
+
|
10
19
|
# from gettext import gettext as _
|
11
|
-
from flask_babelex import gettext as __
|
12
|
-
from flask_babelex import lazy_gettext as _
|
13
|
-
from hiddifypanel.panel import hiddify
|
14
|
-
from hiddifypanel.panel.hiddify import flash
|
15
|
-
from wtforms.fields import StringField, IntegerField, SelectField
|
16
|
-
from flask_bootstrap import SwitchField
|
17
|
-
from wtforms import TextAreaField
|
18
|
-
from wtforms.widgets import TextArea
|
19
|
-
from flask_admin.contrib.sqla import ModelView
|
20
20
|
|
21
21
|
|
22
22
|
class DaysLeftField(IntegerField):
|
hiddifypanel/panel/hiddify.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
import socket
|
2
2
|
from sqlalchemy.orm import Load
|
3
|
+
import glob
|
4
|
+
import json
|
3
5
|
from babel.dates import format_timedelta as babel_format_timedelta
|
4
6
|
from flask_babelex import gettext as __
|
5
7
|
from flask_babelex import lazy_gettext as _
|
6
8
|
from hiddifypanel.models import *
|
7
9
|
from hiddifypanel.panel.database import db
|
8
10
|
import datetime
|
9
|
-
from flask import jsonify, g, url_for, Markup, abort
|
11
|
+
from flask import jsonify, g, url_for, Markup, abort, current_app
|
10
12
|
from flask import flash as flask_flash
|
11
13
|
from wtforms.validators import ValidationError
|
12
14
|
import requests
|
@@ -667,3 +669,23 @@ def generate_x25519_keys():
|
|
667
669
|
|
668
670
|
# Return the keys as a tuple
|
669
671
|
return {"private_key": private_key, "public_key": public_key}
|
672
|
+
|
673
|
+
|
674
|
+
def get_hostkeys(dojson=False):
|
675
|
+
key_files = glob.glob(current_app.config['HIDDIFY_CONFIG_PATH'] + "/other/ssh/host_key/*_key.pub")
|
676
|
+
host_keys = []
|
677
|
+
for file_name in key_files:
|
678
|
+
with open(file_name, "r") as f:
|
679
|
+
host_key = f.read().strip()
|
680
|
+
host_key = host_key.split()
|
681
|
+
if len(host_key) > 2:
|
682
|
+
host_key = host_key[:2] # strip the hostname part
|
683
|
+
host_key = " ".join(host_key)
|
684
|
+
host_keys.append(host_key)
|
685
|
+
if dojson:
|
686
|
+
return json.dumps(host_keys)
|
687
|
+
return host_keys
|
688
|
+
|
689
|
+
|
690
|
+
def get_ssh_client_version(user):
|
691
|
+
return 'SSH-2.0-OpenSSH_7.4p1'
|
hiddifypanel/panel/init_db.py
CHANGED
@@ -30,6 +30,8 @@ def init_db():
|
|
30
30
|
|
31
31
|
add_column(Domain.grpc)
|
32
32
|
add_column(ParentDomain.alias)
|
33
|
+
add_column(User.ed25519_private_key)
|
34
|
+
add_column(User.ed25519_public_key)
|
33
35
|
add_column(User.start_date)
|
34
36
|
add_column(User.package_days)
|
35
37
|
add_column(User.telegram_id)
|
@@ -108,11 +110,34 @@ def init_db():
|
|
108
110
|
return BoolConfig.query.all()
|
109
111
|
|
110
112
|
|
113
|
+
def _v44():
|
114
|
+
import ed25519
|
115
|
+
for u in User.query.all():
|
116
|
+
|
117
|
+
privkey = ed25519.Ed25519PrivateKey.generate()
|
118
|
+
pubkey = privkey.public_key()
|
119
|
+
priv_bytes = privkey.private_bytes(
|
120
|
+
encoding=serialization.Encoding.PEM,
|
121
|
+
format=serialization.PrivateFormat.OpenSSH,
|
122
|
+
encryption_algorithm=serialization.NoEncryption(),
|
123
|
+
)
|
124
|
+
pub_bytes = pubkey.public_bytes(
|
125
|
+
encoding=serialization.Encoding.OpenSSH,
|
126
|
+
format=serialization.PublicFormat.OpenSSH,
|
127
|
+
)
|
128
|
+
u.ed25519_private_key = priv_bytes.decode()
|
129
|
+
u.ed25519_public_key = pub_bytes.decode()
|
130
|
+
db.session.add(Proxy(l3='ssh', transport='ssh', cdn='direct', proto='ssh', enable=True, name="SSH"))
|
131
|
+
add_config_if_not_exist(ConfigEnum.ssh_server_redis_url, "unix:///opt/hiddify-config/other/redis/run.sock?db=1")
|
132
|
+
add_config_if_not_exist(ConfigEnum.ssh_server_port, random.randint(5000, 20000))
|
133
|
+
add_config_if_not_exist(ConfigEnum.ssh_server_enable, False)
|
111
134
|
# def _v43():
|
112
135
|
# if not (Domain.query.filter(Domain.domain==hconfig(ConfigEnum.domain_fronting_domain)).first()):
|
113
136
|
# db.session.add(Domain(domain=hconfig(ConfigEnum.domain_fronting_domain),servernames=hconfig(ConfigEnum.domain_fronting_domain),mode=DomainType.cdn))
|
114
137
|
|
115
138
|
# v7.0.0
|
139
|
+
|
140
|
+
|
116
141
|
def _v42():
|
117
142
|
|
118
143
|
for k in [ConfigEnum.telegram_fakedomain, ConfigEnum.ssfaketls_fakedomain, ConfigEnum.shadowtls_fakedomain]:
|
@@ -366,7 +391,8 @@ def get_proxy_rows_v1():
|
|
366
391
|
"restls1_2 direct ss",
|
367
392
|
"restls1_3 direct ss",
|
368
393
|
"tcp direct ssr",
|
369
|
-
"WS CDN v2ray"
|
394
|
+
"WS CDN v2ray"
|
395
|
+
]
|
370
396
|
)
|
371
397
|
|
372
398
|
|
hiddifypanel/panel/usage.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from sqlalchemy.orm import Load
|
2
|
-
from hiddifypanel import
|
2
|
+
from hiddifypanel.drivers import user_driver
|
3
3
|
from sqlalchemy import func
|
4
4
|
from flask_babelex import gettext as __
|
5
5
|
from flask_babelex import lazy_gettext as _
|
@@ -15,15 +15,8 @@ to_gig_d = 1024**3
|
|
15
15
|
|
16
16
|
|
17
17
|
def update_local_usage():
|
18
|
-
|
19
|
-
res =
|
20
|
-
have_change = False
|
21
|
-
for user in User.query.all():
|
22
|
-
d = xray_api.get_usage(user.uuid, reset=True)
|
23
|
-
sd = singbox_api.get_usage(user.uuid, reset=True)
|
24
|
-
res[user] = {'usage': (d or 0)+(sd or 0), 'ips': ''}
|
25
|
-
|
26
|
-
return add_users_usage(res, 0)
|
18
|
+
res = user_driver.get_users_usage(reset=True)
|
19
|
+
return add_users_usage(res, child_id=0)
|
27
20
|
|
28
21
|
# return {"status": 'success', "comments":res}
|
29
22
|
|
@@ -34,10 +27,6 @@ def add_users_usage_uuid(uuids_bytes, child_id):
|
|
34
27
|
add_users_usage(dbusers_bytes, child_id)
|
35
28
|
|
36
29
|
|
37
|
-
def is_already_valid(uuid):
|
38
|
-
xray_api.get_xray_client().add_client(t, f'{uuid}', f'{uuid}@hiddify.com', protocol=protocol, flow='xtls-rprx-vision', alter_id=0, cipher='chacha20_poly1305')
|
39
|
-
|
40
|
-
|
41
30
|
def add_users_usage(dbusers_bytes, child_id):
|
42
31
|
print(dbusers_bytes)
|
43
32
|
if not hconfig(ConfigEnum.is_parent) and hconfig(ConfigEnum.parent_panel):
|
@@ -46,7 +35,7 @@ def add_users_usage(dbusers_bytes, child_id):
|
|
46
35
|
|
47
36
|
res = {}
|
48
37
|
have_change = False
|
49
|
-
before_enabled_users =
|
38
|
+
before_enabled_users = user_driver.get_enabled_users()
|
50
39
|
daily_usage = {}
|
51
40
|
today = datetime.date.today()
|
52
41
|
for adm in AdminUser.query.all():
|
@@ -73,7 +62,7 @@ def add_users_usage(dbusers_bytes, child_id):
|
|
73
62
|
detail.current_usage_GB = 0
|
74
63
|
|
75
64
|
if before_enabled_users[user.uuid] == 0 and user.is_active:
|
76
|
-
|
65
|
+
user_driver.add_client(user)
|
77
66
|
send_bot_message(user)
|
78
67
|
have_change = True
|
79
68
|
if type(usage_bytes) != int or usage_bytes == 0:
|
@@ -91,7 +80,7 @@ def add_users_usage(dbusers_bytes, child_id):
|
|
91
80
|
user.start_date = datetime.date.today()
|
92
81
|
|
93
82
|
if before_enabled_users[user.uuid] == 1 and not is_user_active(user):
|
94
|
-
|
83
|
+
user_driver.remove_client(user)
|
95
84
|
have_change = True
|
96
85
|
res[user.uuid] = f"{res[user.uuid]} !OUT of USAGE! Client Removed"
|
97
86
|
|