hiddifypanel 10.70.9__py3-none-any.whl → 10.80.0.dev1__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 +5 -2
- hiddifypanel/__init__.py +5 -1
- hiddifypanel/auth.py +8 -3
- hiddifypanel/base.py +26 -23
- hiddifypanel/cache.py +2 -1
- hiddifypanel/drivers/ssh_liberty_bridge_api.py +2 -1
- hiddifypanel/drivers/wireguard_api.py +1 -1
- hiddifypanel/hutils/crypto.py +27 -0
- hiddifypanel/hutils/flask.py +5 -3
- hiddifypanel/hutils/network/cf_api.py +5 -5
- hiddifypanel/hutils/proxy/clash.py +1 -1
- hiddifypanel/hutils/proxy/shared.py +8 -12
- hiddifypanel/hutils/proxy/singbox.py +3 -1
- hiddifypanel/hutils/proxy/xray.py +1 -1
- hiddifypanel/models/admin.py +1 -1
- hiddifypanel/models/base_account.py +3 -0
- hiddifypanel/models/config.py +1 -1
- hiddifypanel/models/config_enum.py +13 -0
- hiddifypanel/models/user.py +2 -2
- hiddifypanel/panel/admin/AdminstratorAdmin.py +9 -2
- hiddifypanel/panel/admin/ProxyAdmin.py +4 -0
- hiddifypanel/panel/admin/QuickSetup.py +40 -9
- hiddifypanel/panel/admin/SettingAdmin.py +6 -0
- hiddifypanel/panel/admin/UserAdmin.py +10 -8
- hiddifypanel/panel/admin/adminlte.py +1 -1
- hiddifypanel/panel/cli.py +3 -1
- hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +76 -6
- hiddifypanel/panel/common.py +6 -3
- hiddifypanel/panel/common_bp/login.py +14 -8
- hiddifypanel/panel/init_db.py +139 -62
- hiddifypanel/panel/user/templates/base_singbox_config.json.j2 +16 -0
- hiddifypanel/panel/user/templates/home/home.html +1 -2
- hiddifypanel/panel/user/templates/home/multi.html +1 -2
- hiddifypanel/panel/user/user.py +1 -1
- hiddifypanel/static/apps-icon/singbox.ico +0 -0
- hiddifypanel/templates/fake.html +320 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/en/LC_MESSAGES/messages.po +50 -24
- hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/fa/LC_MESSAGES/messages.po +47 -25
- hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/pt/LC_MESSAGES/messages.po +41 -18
- hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/ru/LC_MESSAGES/messages.po +56 -29
- hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
- hiddifypanel/translations/zh/LC_MESSAGES/messages.po +34 -14
- hiddifypanel/translations.i18n/en.json +27 -11
- hiddifypanel/translations.i18n/fa.json +26 -10
- hiddifypanel/translations.i18n/fr.json +26 -8
- hiddifypanel/translations.i18n/my.json +1266 -0
- hiddifypanel/translations.i18n/pt.json +21 -5
- hiddifypanel/translations.i18n/ru.json +29 -13
- hiddifypanel/translations.i18n/zh.json +20 -4
- hiddifypanel-10.80.0.dev1.dist-info/METADATA +130 -0
- {hiddifypanel-10.70.9.dist-info → hiddifypanel-10.80.0.dev1.dist-info}/RECORD +111 -110
- {hiddifypanel-10.70.9.dist-info → hiddifypanel-10.80.0.dev1.dist-info}/WHEEL +1 -2
- hiddifypanel-10.80.0.dev1.dist-info/entry_points.txt +3 -0
- hiddifypanel-10.70.9.dist-info/METADATA +0 -144
- hiddifypanel-10.70.9.dist-info/entry_points.txt +0 -2
- hiddifypanel-10.70.9.dist-info/top_level.txt +0 -1
- {hiddifypanel-10.70.9.dist-info → hiddifypanel-10.80.0.dev1.dist-info}/LICENSE.md +0 -0
hiddifypanel/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
10.
|
1
|
+
10.80.0.dev7
|
hiddifypanel/VERSION.py
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
-
|
1
|
+
import importlib.metadata
|
2
2
|
from datetime import datetime
|
3
|
-
|
3
|
+
|
4
|
+
__version__ = importlib.metadata.version(__package__ or __name__)
|
5
|
+
__release_time__= datetime.strptime('2024-11-10T19:45:21','%Y-%m-%dT%H:%M:%S')
|
6
|
+
is_released_version=True
|
hiddifypanel/__init__.py
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
from .VERSION import __version__,
|
1
|
+
from .VERSION import __version__, __release_time__,is_released_version
|
2
|
+
|
3
|
+
from dotenv import load_dotenv
|
4
|
+
load_dotenv("/opt/hiddify-manager/hiddify-panel/app.cfg")
|
5
|
+
|
2
6
|
# from . import cache
|
3
7
|
from . import Events
|
4
8
|
from .base import create_app, create_app_wsgi
|
hiddifypanel/auth.py
CHANGED
@@ -142,14 +142,19 @@ def get_account_by_uuid(uuid, is_admin):
|
|
142
142
|
return AdminUser.by_uuid(f'{uuid}') if is_admin else User.by_uuid(f'{uuid}')
|
143
143
|
|
144
144
|
|
145
|
-
def login_by_uuid(uuid, is_admin: bool):
|
145
|
+
def login_by_uuid(uuid,password:str, is_admin: bool)->bool:
|
146
146
|
account = get_account_by_uuid(uuid, is_admin)
|
147
147
|
if not account:
|
148
148
|
return False
|
149
|
+
if account.password!=password:
|
150
|
+
return False
|
149
151
|
return login_user(account, force=True)
|
150
152
|
|
151
153
|
|
152
154
|
def auth_before_request():
|
155
|
+
if ".webmanifest" in request.path:
|
156
|
+
return
|
157
|
+
|
153
158
|
# print("before_request")
|
154
159
|
account = None
|
155
160
|
|
@@ -160,7 +165,7 @@ def auth_before_request():
|
|
160
165
|
# print("uuid", g.uuid, is_admin_path)
|
161
166
|
account = get_account_by_uuid(g.uuid, is_admin_path)
|
162
167
|
# print(account)
|
163
|
-
if not account:
|
168
|
+
if not account or account.password!="":
|
164
169
|
return logout_redirect()
|
165
170
|
if is_admin_path:
|
166
171
|
next_url = request.url
|
@@ -230,7 +235,7 @@ def redirect_to_login():
|
|
230
235
|
json_abort(403, 'Unathorized')
|
231
236
|
# if g.user_agent['is_browser']:
|
232
237
|
# return redirect(hurl_for('common_bp.LoginView:basic_0', force=1, next=request.path))
|
233
|
-
return redirect(hurl_for('common_bp.LoginView:index', force=1, next=request.path))
|
238
|
+
return redirect(hurl_for('common_bp.LoginView:index', force=1, next=request.path.replace(f'{g.uuid}/',''),user=g.uuid))
|
234
239
|
|
235
240
|
# else:
|
236
241
|
# abort(401, "Unauthorized")
|
hiddifypanel/base.py
CHANGED
@@ -42,12 +42,13 @@ def create_app(*args, cli=False, **config):
|
|
42
42
|
# app = Flask(__name__, static_url_path="/<proxy_path>/static/", instance_relative_config=True)
|
43
43
|
|
44
44
|
if not cli:
|
45
|
-
|
45
|
+
|
46
46
|
from hiddifypanel import auth
|
47
47
|
app.config["PREFERRED_URL_SCHEME"] = "https"
|
48
48
|
app.wsgi_app = ProxyFix(
|
49
49
|
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1,
|
50
50
|
)
|
51
|
+
app.secret_key="asdsad"
|
51
52
|
app.servers = {
|
52
53
|
'name': 'current',
|
53
54
|
'url': '',
|
@@ -68,18 +69,7 @@ def create_app(*args, cli=False, **config):
|
|
68
69
|
# setup flask server-side session
|
69
70
|
# app.config['APPLICATION_ROOT'] = './'
|
70
71
|
# app.config['SESSION_COOKIE_DOMAIN'] = '/'
|
71
|
-
|
72
|
-
app.config['SESSION_REDIS'] = redis_client
|
73
|
-
app.config['SESSION_PERMANENT'] = True
|
74
|
-
app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(days=10)
|
75
|
-
app.security_schemes = { # equals to use config SECURITY_SCHEMES
|
76
|
-
'Hiddify-API-Key': {
|
77
|
-
'type': 'apiKey',
|
78
|
-
'in': 'header',
|
79
|
-
'name': 'Hiddify-API-Key',
|
80
|
-
}
|
81
|
-
}
|
82
|
-
Session(app)
|
72
|
+
|
83
73
|
|
84
74
|
app.jinja_env.line_statement_prefix = '%'
|
85
75
|
from hiddifypanel import hutils
|
@@ -112,6 +102,19 @@ def create_app(*args, cli=False, **config):
|
|
112
102
|
app.jinja_env.globals['get_locale'] = get_locale
|
113
103
|
babel = Babel(app, locale_selector=get_locale)
|
114
104
|
if not cli:
|
105
|
+
app.config['SESSION_TYPE'] = 'redis'
|
106
|
+
from hiddifypanel.cache import redis_client
|
107
|
+
app.config['SESSION_REDIS'] = redis_client
|
108
|
+
app.config['SESSION_PERMANENT'] = True
|
109
|
+
app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(days=10)
|
110
|
+
app.security_schemes = { # equals to use config SECURITY_SCHEMES
|
111
|
+
'Hiddify-API-Key': {
|
112
|
+
'type': 'apiKey',
|
113
|
+
'in': 'header',
|
114
|
+
'name': 'Hiddify-API-Key',
|
115
|
+
}
|
116
|
+
}
|
117
|
+
Session(app)
|
115
118
|
hiddifypanel.panel.common.init_app(app)
|
116
119
|
hiddifypanel.panel.common_bp.init_app(app)
|
117
120
|
|
@@ -122,7 +125,7 @@ def create_app(*args, cli=False, **config):
|
|
122
125
|
|
123
126
|
app.config.update(config) # Override with passed config
|
124
127
|
# app.config['WTF_CSRF_CHECK_DEFAULT'] = False
|
125
|
-
app.config['WTF_CSRF_ENABLED'] = False
|
128
|
+
# app.config['WTF_CSRF_ENABLED'] = False
|
126
129
|
# app.config['BABEL_TRANSLATION_DIRECTORIES'] = '/workspace/Hiddify-Server/hiddify-panel/src/translations.i18n'
|
127
130
|
|
128
131
|
# from flask_wtf.csrf import CSRFProtect
|
@@ -131,15 +134,15 @@ def create_app(*args, cli=False, **config):
|
|
131
134
|
|
132
135
|
# @app.before_request
|
133
136
|
# def check_csrf():
|
134
|
-
# if "/admin/user/" in request.base_url:
|
135
|
-
# return
|
136
|
-
# if "/admin/domain/" in request.base_url:
|
137
|
-
# return
|
138
|
-
# if "/admin/actions/" in request.base_url:
|
139
|
-
# return
|
140
|
-
# if "/api/" in request.base_url:
|
141
|
-
# return
|
142
|
-
#
|
137
|
+
# # if "/admin/user/" in request.base_url:
|
138
|
+
# # return
|
139
|
+
# # if "/admin/domain/" in request.base_url:
|
140
|
+
# # return
|
141
|
+
# # if "/admin/actions/" in request.base_url:
|
142
|
+
# # return
|
143
|
+
# # if "/api/" in request.base_url:
|
144
|
+
# # return
|
145
|
+
# csrf.protect()
|
143
146
|
|
144
147
|
hiddifypanel.panel.cli.init_app(app)
|
145
148
|
return app
|
hiddifypanel/cache.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
import os
|
1
2
|
from redis_cache import RedisCache, chunks, compact_dump
|
2
3
|
import redis
|
3
4
|
from pickle import dumps, loads
|
4
5
|
from loguru import logger
|
5
6
|
|
6
|
-
redis_client = redis.from_url(
|
7
|
+
redis_client = redis.from_url(os.environ["REDIS_URI_MAIN"])
|
7
8
|
|
8
9
|
|
9
10
|
class CustomRedisCache(RedisCache):
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import os
|
1
2
|
from .abstract_driver import DriverABS
|
2
3
|
from hiddifypanel.models import *
|
3
4
|
import redis
|
@@ -13,7 +14,7 @@ class SSHLibertyBridgeApi(DriverABS):
|
|
13
14
|
|
14
15
|
def get_ssh_redis_client(self):
|
15
16
|
if not hasattr(self, 'redis_client'):
|
16
|
-
self.redis_client = redis.from_url(
|
17
|
+
self.redis_client = redis.from_url(os.environ.get("REDIS_URI_SSH",""), decode_responses=True)
|
17
18
|
|
18
19
|
return self.redis_client
|
19
20
|
|
@@ -13,7 +13,7 @@ USERS_USAGE = "wg:users-usage"
|
|
13
13
|
class WireguardApi(DriverABS):
|
14
14
|
def get_redis_client(self):
|
15
15
|
if not hasattr(self, 'redis_client'):
|
16
|
-
self.redis_client = redis.from_url(
|
16
|
+
self.redis_client = redis.from_url(os.environ.get("REDIS_URI_SSH",""))
|
17
17
|
|
18
18
|
return self.redis_client
|
19
19
|
|
hiddifypanel/hutils/crypto.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import os
|
1
2
|
import subprocess
|
2
3
|
from cryptography.hazmat.primitives import serialization
|
3
4
|
from cryptography.hazmat.primitives.asymmetric import x25519, ed25519
|
@@ -46,3 +47,29 @@ def generate_x25519_keys():
|
|
46
47
|
priv_str = base64.urlsafe_b64encode(priv_bytes).decode()[:-1]
|
47
48
|
|
48
49
|
return {'private_key': priv_str, 'public_key': pub_str}
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
def generate_ssh_host_keys():
|
54
|
+
key_types = ["dsa", "ecdsa", "ed25519", "rsa"]
|
55
|
+
keys_dict = {}
|
56
|
+
|
57
|
+
# Generate and read keys
|
58
|
+
for key_type in key_types:
|
59
|
+
key_file = f"ssh_host_{key_type}_key"
|
60
|
+
|
61
|
+
subprocess.run([
|
62
|
+
"ssh-keygen", "-t", key_type,
|
63
|
+
"-f", key_file,
|
64
|
+
"-N", ""
|
65
|
+
], check=True)
|
66
|
+
|
67
|
+
keys_dict[key_type]={}
|
68
|
+
with open(key_file, "r") as f:
|
69
|
+
keys_dict[key_type]['pk'] = f.read()
|
70
|
+
with open(f"{key_file}.pub", "r") as f:
|
71
|
+
keys_dict[key_type]['pub'] = f.read()
|
72
|
+
|
73
|
+
os.remove(key_file)
|
74
|
+
os.remove(f"{key_file}.pub") # Remove the public key if not needed
|
75
|
+
return keys_dict
|
hiddifypanel/hutils/flask.py
CHANGED
@@ -51,7 +51,7 @@ def hurl_for(endpoint, **values):
|
|
51
51
|
def get_user_agent() -> dict:
|
52
52
|
ua = __parse_user_agent(request.user_agent.string)
|
53
53
|
|
54
|
-
if ua.get('v', 1) <
|
54
|
+
if ua.get('v', 1) < 8:
|
55
55
|
__parse_user_agent.invalidate_all() # type:ignore
|
56
56
|
ua = __parse_user_agent(request.user_agent.string)
|
57
57
|
return ua
|
@@ -65,13 +65,14 @@ def __parse_user_agent(ua: str) -> dict:
|
|
65
65
|
# Example: SFA/1.8.0 (239; sing-box 1.8.0)
|
66
66
|
# Example: SFA/1.7.0 (239; sing-box 1.7.0)
|
67
67
|
# Example: HiddifyNext/0.13.6 (android) like ClashMeta v2ray sing-box
|
68
|
-
|
68
|
+
if ua=="v2rayNG/1.8.23": #temporary fix for xray sub in hiddifynext
|
69
|
+
ua="HiddifyNextX/0.13.6 (android) like ClashMeta v2ray sing-box"
|
69
70
|
uaa = user_agents.parse(ua)
|
70
71
|
|
71
72
|
match = re.search(ua_version_pattern, ua)
|
72
73
|
generic_version = list(map(int, match.group(1).split('.'))) if match else [0, 0, 0]
|
73
74
|
res = {}
|
74
|
-
res['v'] =
|
75
|
+
res['v'] = 8
|
75
76
|
res["is_bot"] = uaa.is_bot
|
76
77
|
res["is_browser"] = re.match('^Mozilla', ua, re.IGNORECASE) and True
|
77
78
|
res['os'] = uaa.os.family
|
@@ -80,6 +81,7 @@ def __parse_user_agent(ua: str) -> dict:
|
|
80
81
|
res['is_clash_meta'] = re.match('^(Clash-verge|Clash-?Meta|Stash|NekoBox|NekoRay|Pharos|hiddify-desktop)', ua, re.IGNORECASE) and True
|
81
82
|
res['is_singbox'] = re.match('^(HiddifyNext|Dart|SFI|SFA)', ua, re.IGNORECASE) and True
|
82
83
|
res['is_hiddify'] = re.match('^(HiddifyNext)', ua, re.IGNORECASE) and True
|
84
|
+
res['is_hiddify_prefere_xray'] = re.match('^(HiddifyNextX)', ua, re.IGNORECASE) and True
|
83
85
|
res['is_streisand'] = re.match('^(Streisand)', ua, re.IGNORECASE) and True
|
84
86
|
res['is_shadowrocket'] = re.match('^(Shadowrocket)', ua, re.IGNORECASE) and True
|
85
87
|
res['is_v2rayng'] = re.match('^(v2rayNG)', ua, re.IGNORECASE) and True
|
@@ -1,18 +1,18 @@
|
|
1
|
-
import
|
1
|
+
import cloudflare
|
2
2
|
from hiddifypanel.models import hconfig, ConfigEnum
|
3
3
|
|
4
|
-
__cf:
|
4
|
+
__cf: cloudflare.Cloudflare # type: ignore
|
5
5
|
|
6
6
|
|
7
7
|
def __prepare_cf_api_client() -> bool:
|
8
8
|
'''Prepares cloudflare client if it's not already'''
|
9
9
|
global __cf
|
10
|
-
if __cf and isinstance(__cf,
|
10
|
+
if __cf and isinstance(__cf, cloudflare.Cloudflare):
|
11
11
|
return True
|
12
12
|
|
13
13
|
if hconfig(ConfigEnum.cloudflare):
|
14
|
-
__cf =
|
15
|
-
if __cf and isinstance(__cf,
|
14
|
+
__cf = cloudflare.Cloudflare(token=hconfig(ConfigEnum.cloudflare))
|
15
|
+
if __cf and isinstance(__cf, cloudflare.Cloudflare):
|
16
16
|
return True
|
17
17
|
return False
|
18
18
|
|
@@ -57,7 +57,7 @@ def to_clash(proxy, meta_or_normal):
|
|
57
57
|
if proxy["proto"] == "ssh":
|
58
58
|
base["username"] = proxy["uuid"]
|
59
59
|
base["private-key"] = proxy['private_key']
|
60
|
-
base["host-key"] = proxy.get('
|
60
|
+
base["host-key"] = proxy.get('host_keys', [])
|
61
61
|
return base
|
62
62
|
base["udp"] = True
|
63
63
|
if proxy["proto"] == ProxyProto.wireguard:
|
@@ -11,17 +11,13 @@ from hiddifypanel.models import Proxy, ProxyProto, ProxyL3, ProxyTransport, Prox
|
|
11
11
|
from hiddifypanel import hutils
|
12
12
|
|
13
13
|
|
14
|
-
def get_ssh_hostkeys(dojson=False) -> list[str] | str:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
if len(host_key) > 2:
|
22
|
-
host_key = host_key[:2] # strip the hostname part
|
23
|
-
host_key = " ".join(host_key)
|
24
|
-
host_keys.append(host_key)
|
14
|
+
def get_ssh_hostkeys(hconfigs,dojson=False) -> list[str] | str:
|
15
|
+
host_keys = [
|
16
|
+
# hconfigs[ConfigEnum.ssh_host_dsa_pub],
|
17
|
+
hconfigs[ConfigEnum.ssh_host_ed25519_pub],
|
18
|
+
hconfigs[ConfigEnum.ssh_host_ecdsa_pub],
|
19
|
+
hconfigs[ConfigEnum.ssh_host_rsa_pub],
|
20
|
+
]
|
25
21
|
if dojson:
|
26
22
|
return json.dumps(host_keys)
|
27
23
|
return host_keys
|
@@ -441,7 +437,7 @@ def make_proxy(hconfigs: dict, proxy: Proxy, domain_db: Domain, phttp=80, ptls=4
|
|
441
437
|
return base
|
442
438
|
if ProxyProto.ssh == proxy.proto:
|
443
439
|
base['private_key'] = g.account.ed25519_private_key
|
444
|
-
base['
|
440
|
+
base['host_keys'] = hutils.proxy.get_ssh_hostkeys(hconfigs,False)
|
445
441
|
# base['ssh_port'] = hconfig(ConfigEnum.ssh_server_port)
|
446
442
|
return base
|
447
443
|
return {'name': name, 'msg': 'not valid', 'type': 'error', 'proto': proxy.proto}
|
@@ -45,6 +45,8 @@ def configs_as_json(domains: list[Domain], **kwargs) -> str:
|
|
45
45
|
|
46
46
|
|
47
47
|
def is_xray_proxy(proxy: dict):
|
48
|
+
if g.user_agent.get('is_hiddify_prefere_xray'):
|
49
|
+
return True
|
48
50
|
if proxy['transport'] == ProxyTransport.splithttp:
|
49
51
|
return True
|
50
52
|
return False
|
@@ -323,7 +325,7 @@ def add_ssh(all_base: list[dict], proxy: dict):
|
|
323
325
|
base["user"] = proxy['uuid']
|
324
326
|
base["private_key"] = proxy['private_key'] # .replace('\n', '\\n')
|
325
327
|
|
326
|
-
base["host_key"] = proxy.get('
|
328
|
+
base["host_key"] = proxy.get('host_keys', [])
|
327
329
|
|
328
330
|
socks_front = {
|
329
331
|
"type": "socks",
|
@@ -63,7 +63,7 @@ def to_link(proxy: dict) -> str | dict:
|
|
63
63
|
streisand_ssh = hutils.encode.do_base_64(f'{proxy["uuid"]}:0:{proxy["private_key"]}::@{proxy["server"]}:{proxy["port"]}')
|
64
64
|
baseurl += f'{streisand_ssh}#{name_link}'
|
65
65
|
else:
|
66
|
-
hk = ",".join(proxy["
|
66
|
+
hk = ",".join(proxy["host_keys"])
|
67
67
|
pk = proxy["private_key"].replace('\n', '')
|
68
68
|
baseurl += f'{proxy["uuid"]}@{proxy["server"]}:{proxy["port"]}/?file=ssh&pk={pk}&hk={hk}#{name_link}'
|
69
69
|
|
hiddifypanel/models/admin.py
CHANGED
@@ -41,6 +41,9 @@ class BaseAccount(db.Model, SerializerMixin, FlaskLoginUserMixin): # type: igno
|
|
41
41
|
'telegram_id': self.telegram_id,
|
42
42
|
'lang': self.lang
|
43
43
|
}
|
44
|
+
def update_password(self,new_password):
|
45
|
+
self.password=new_password
|
46
|
+
db.session.commit()
|
44
47
|
|
45
48
|
@classmethod
|
46
49
|
def by_id(cls, id: int):
|
hiddifypanel/models/config.py
CHANGED
@@ -39,7 +39,7 @@ class StrConfig(db.Model, SerializerMixin):
|
|
39
39
|
child_id = Column(Integer, ForeignKey('child.id'), primary_key=True, default=0)
|
40
40
|
# category = db.Column(db.String(128), primary_key=True)
|
41
41
|
key = Column(Enum(ConfigEnum), primary_key=True, default=ConfigEnum.admin_secret)
|
42
|
-
value = Column(String(
|
42
|
+
value = Column(String(3072))
|
43
43
|
|
44
44
|
def to_dict(self: "StrConfig"):
|
45
45
|
return {
|
@@ -277,6 +277,19 @@ class ConfigEnum(metaclass=FastEnum):
|
|
277
277
|
sub_full_clash_enable = _BoolConfigDscr(ConfigCategory.hidden)
|
278
278
|
sub_full_clash_meta_enable = _BoolConfigDscr(ConfigCategory.hidden)
|
279
279
|
|
280
|
+
|
281
|
+
#ssh host keys
|
282
|
+
ssh_host_rsa_pk = _StrConfigDscr(ConfigCategory.hidden)
|
283
|
+
ssh_host_rsa_pub = _StrConfigDscr(ConfigCategory.hidden)
|
284
|
+
ssh_host_ed25519_pk = _StrConfigDscr(ConfigCategory.hidden)
|
285
|
+
ssh_host_ed25519_pub = _StrConfigDscr(ConfigCategory.hidden)
|
286
|
+
ssh_host_ecdsa_pk = _StrConfigDscr(ConfigCategory.hidden)
|
287
|
+
ssh_host_ecdsa_pub = _StrConfigDscr(ConfigCategory.hidden)
|
288
|
+
ssh_host_dsa_pk = _StrConfigDscr(ConfigCategory.hidden)
|
289
|
+
ssh_host_dsa_pub = _StrConfigDscr(ConfigCategory.hidden)
|
290
|
+
|
291
|
+
|
292
|
+
|
280
293
|
hiddifycli_enable = _BoolConfigDscr(ConfigCategory.hidden, ApplyMode.reinstall)
|
281
294
|
|
282
295
|
@classmethod
|
hiddifypanel/models/user.py
CHANGED
@@ -76,7 +76,7 @@ class User(BaseAccount, SerializerMixin):
|
|
76
76
|
current_usage = db.Column(db.BigInteger, default=0, nullable=False)
|
77
77
|
last_reset_time = db.Column(db.Date, default=datetime.date.today())
|
78
78
|
added_by = db.Column(db.Integer, db.ForeignKey('admin_user.id'), default=1)
|
79
|
-
max_ips = db.Column(db.Integer, default=
|
79
|
+
max_ips = db.Column(db.Integer, default=100, nullable=False)
|
80
80
|
details = db.relationship('UserDetail', cascade="all,delete", backref='user', lazy='dynamic',)
|
81
81
|
enable = db.Column(db.Boolean, default=True, nullable=False)
|
82
82
|
ed25519_private_key = db.Column(db.String(500), default="")
|
@@ -347,6 +347,6 @@ class User(BaseAccount, SerializerMixin):
|
|
347
347
|
def on_user_insert(mapper, connection, target):
|
348
348
|
from hiddifypanel import hutils
|
349
349
|
hutils.model.gen_username(target)
|
350
|
-
hutils.model.gen_password(target)
|
350
|
+
# hutils.model.gen_password(target)
|
351
351
|
hutils.model.gen_ed25519_keys(target)
|
352
352
|
hutils.model.gen_wg_keys(target)
|
@@ -40,7 +40,7 @@ class SubAdminsField(SelectField):
|
|
40
40
|
class AdminstratorAdmin(AdminLTEModelView):
|
41
41
|
column_hide_backrefs = False
|
42
42
|
column_list = ["name", 'UserLinks', 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'online_users', 'comment',]
|
43
|
-
form_columns = ["name", 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'comment', "uuid"]
|
43
|
+
form_columns = ["name", 'mode', 'can_add_admin', 'max_active_users', 'max_users', 'comment', "uuid", "password"]
|
44
44
|
list_template = 'model/admin_list.html'
|
45
45
|
# column_editable_list = ['name']
|
46
46
|
# edit_modal = True
|
@@ -59,6 +59,7 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
59
59
|
"comment": _("Note"),
|
60
60
|
'max_active_users': _("Max Active Users"),
|
61
61
|
'max_users': _('Max Users'),
|
62
|
+
"password":_("user.password.title"),
|
62
63
|
"online_users": _("Online Users"),
|
63
64
|
'can_add_admin': _("Can add sub admin")
|
64
65
|
|
@@ -204,6 +205,7 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
204
205
|
# else:
|
205
206
|
# model.parent_admin_id=1
|
206
207
|
# model.parent_admin=AdminUser.query.filter(AdminUser.id==1).first()
|
208
|
+
|
207
209
|
if model.id != 1 and model.parent_admin is None:
|
208
210
|
model.parent_admin_id = g.account.id
|
209
211
|
model.parent_admin = g.account
|
@@ -212,12 +214,17 @@ class AdminstratorAdmin(AdminLTEModelView):
|
|
212
214
|
raise ValidationError("Sub-Admin can not have more power!!!!")
|
213
215
|
if g.account.mode == AdminMode.agent and model.mode != AdminMode.agent:
|
214
216
|
raise ValidationError("Sub-Admin can not have more power!!!!")
|
217
|
+
|
218
|
+
if not model.password and not is_created:
|
219
|
+
model.password=AdminUser.by_id(model.id).password
|
220
|
+
|
215
221
|
|
216
222
|
def on_model_delete(self, model):
|
217
223
|
model.remove()
|
218
224
|
|
219
225
|
def on_form_prefill(self, form, id=None):
|
220
|
-
|
226
|
+
|
227
|
+
form.password.data=""
|
221
228
|
if g.account.mode != AdminMode.super_admin:
|
222
229
|
del form.mode
|
223
230
|
del form.can_add_admin
|
@@ -96,6 +96,8 @@ def get_all_proxy_form(empty=False):
|
|
96
96
|
|
97
97
|
for cdn in categories1:
|
98
98
|
class CDNForm(FlaskForm):
|
99
|
+
class Meta:
|
100
|
+
csrf = False
|
99
101
|
pass
|
100
102
|
cdn_proxies = [c for c in proxies if c.cdn == cdn]
|
101
103
|
pgroup = {
|
@@ -107,6 +109,8 @@ def get_all_proxy_form(empty=False):
|
|
107
109
|
protos = sorted([c for c in {pgroup.get(c.proto, c.proto): 1 for c in cdn_proxies}])
|
108
110
|
for proto in protos:
|
109
111
|
class ProtoForm(FlaskForm):
|
112
|
+
class Meta:
|
113
|
+
csrf = False
|
110
114
|
pass
|
111
115
|
proto_proxies = [c for c in cdn_proxies if pgroup.get(c.proto, c.proto) == proto]
|
112
116
|
for proxy in proto_proxies:
|
@@ -10,7 +10,7 @@ from flask_wtf import FlaskForm
|
|
10
10
|
from flask_bootstrap import SwitchField
|
11
11
|
from hiddifypanel.panel import hiddify
|
12
12
|
from flask_classful import FlaskView
|
13
|
-
from wtforms.validators import ValidationError
|
13
|
+
from wtforms.validators import ValidationError, Length, InputRequired
|
14
14
|
# from gettext import gettext as _
|
15
15
|
|
16
16
|
from hiddifypanel.models import Domain, DomainType, StrConfig, ConfigEnum, get_hconfigs
|
@@ -28,12 +28,14 @@ class QuickSetup(FlaskView):
|
|
28
28
|
if next:
|
29
29
|
step = step + 1
|
30
30
|
form = {1: get_lang_form,
|
31
|
-
2:
|
32
|
-
3:
|
31
|
+
2: get_password_form,
|
32
|
+
3: get_quick_setup_form,
|
33
|
+
4: get_proxy_form}
|
33
34
|
|
34
35
|
return form[step](empty=empty or next)
|
35
36
|
|
36
37
|
def index(self):
|
38
|
+
|
37
39
|
return render_template(
|
38
40
|
'quick_setup.html',
|
39
41
|
form=self.current_form(),
|
@@ -45,11 +47,12 @@ class QuickSetup(FlaskView):
|
|
45
47
|
def post(self):
|
46
48
|
if request.args.get('changepw') == "true":
|
47
49
|
AdminUser.current_admin_or_owner().uuid = str(uuid.uuid4())
|
50
|
+
# AdminUser.current_admin_or_owner().password = hutils.random.get_random_password()
|
48
51
|
db.session.commit()
|
49
52
|
|
50
53
|
set_hconfig(ConfigEnum.first_setup, False)
|
51
54
|
form = self.current_form()
|
52
|
-
if not form.validate_on_submit() or form.step.data not in ["1", "2", "3"]:
|
55
|
+
if not form.validate_on_submit() or form.step.data not in ["1", "2", "3","4"]:
|
53
56
|
hutils.flask.flash(_('config.validation-error'), 'danger')
|
54
57
|
return render_template(
|
55
58
|
'quick_setup.html', form=form,
|
@@ -84,9 +87,9 @@ def get_lang_form(empty=False):
|
|
84
87
|
|
85
88
|
return render_template(
|
86
89
|
'quick_setup.html', form=view.current_form(next=True),
|
87
|
-
admin_link=admin_link(),
|
88
|
-
ipv4=hutils.network.get_ip_str(4),
|
89
|
-
ipv6=hutils.network.get_ip_str(6),
|
90
|
+
# admin_link=admin_link(),
|
91
|
+
# ipv4=hutils.network.get_ip_str(4),
|
92
|
+
# ipv6=hutils.network.get_ip_str(6),
|
90
93
|
show_domain_info=False)
|
91
94
|
|
92
95
|
form = LangForm(None)if empty else LangForm()
|
@@ -94,6 +97,34 @@ def get_lang_form(empty=False):
|
|
94
97
|
return form
|
95
98
|
|
96
99
|
|
100
|
+
def get_password_form(empty=False):
|
101
|
+
class PasswordForm(FlaskForm):
|
102
|
+
step = wtf.HiddenField(default="1")
|
103
|
+
admin_pass = wtf.PasswordField(
|
104
|
+
_("user.password.title"),
|
105
|
+
description=_("user.password.description"),
|
106
|
+
default="",validators=[
|
107
|
+
|
108
|
+
InputRequired(message=_("user.password.validation-required")),
|
109
|
+
Length(min=8, message=_("user.password.validation-lenght"))
|
110
|
+
|
111
|
+
])
|
112
|
+
password_submit = wtf.SubmitField(_('Submit'))
|
113
|
+
|
114
|
+
def post(self, view):
|
115
|
+
AdminUser.current_admin_or_owner().update_password(self.admin_pass.data)
|
116
|
+
|
117
|
+
return render_template(
|
118
|
+
'quick_setup.html', form=view.current_form(next=True),
|
119
|
+
admin_link=admin_link(),
|
120
|
+
ipv4=hutils.network.get_ip_str(4),
|
121
|
+
ipv6=hutils.network.get_ip_str(6),
|
122
|
+
show_domain_info=False)
|
123
|
+
|
124
|
+
form = PasswordForm(None)if empty else PasswordForm()
|
125
|
+
form.step.data = "2"
|
126
|
+
return form
|
127
|
+
|
97
128
|
def get_proxy_form(empty=False):
|
98
129
|
class ProxyForm(FlaskForm):
|
99
130
|
step = wtf.HiddenField(default="3")
|
@@ -127,7 +158,7 @@ def get_proxy_form(empty=False):
|
|
127
158
|
setattr(ProxyForm, f'{cf.key}', field)
|
128
159
|
setattr(ProxyForm, "submit_global", wtf.fields.SubmitField(_('Submit')))
|
129
160
|
form = ProxyForm(None) if empty else ProxyForm()
|
130
|
-
form.step.data = "
|
161
|
+
form.step.data = "4"
|
131
162
|
return form
|
132
163
|
|
133
164
|
|
@@ -210,7 +241,7 @@ def get_quick_setup_form(empty=False):
|
|
210
241
|
show_domain_info=False)
|
211
242
|
|
212
243
|
form = BasicConfigs(None) if empty else BasicConfigs()
|
213
|
-
form.step.data = "
|
244
|
+
form.step.data = "3"
|
214
245
|
return form
|
215
246
|
|
216
247
|
|
@@ -137,6 +137,10 @@ class SettingAdmin(FlaskView):
|
|
137
137
|
form = get_config_form()
|
138
138
|
else:
|
139
139
|
hutils.flask.flash(_('config.validation-error'), 'danger') # type: ignore
|
140
|
+
for field, errors in form.errors.items():
|
141
|
+
for error in errors:
|
142
|
+
hutils.flask.flash(error, 'danger') # type: ignore
|
143
|
+
|
140
144
|
|
141
145
|
return reset_action or render_template('config.html', form=form)
|
142
146
|
|
@@ -185,6 +189,8 @@ def get_config_form():
|
|
185
189
|
continue
|
186
190
|
|
187
191
|
class CategoryForm(FlaskForm):
|
192
|
+
class Meta:
|
193
|
+
csrf = False
|
188
194
|
description_for_fieldset = wtf.TextAreaField("", description=_(f'config.{cat}.description'), render_kw={"class": "d-none"})
|
189
195
|
for c2 in cat_configs:
|
190
196
|
if not (c2 in configs_key):
|