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.
Files changed (62) hide show
  1. hiddifypanel/VERSION +1 -1
  2. hiddifypanel/VERSION.py +5 -2
  3. hiddifypanel/__init__.py +5 -1
  4. hiddifypanel/auth.py +8 -3
  5. hiddifypanel/base.py +26 -23
  6. hiddifypanel/cache.py +2 -1
  7. hiddifypanel/drivers/ssh_liberty_bridge_api.py +2 -1
  8. hiddifypanel/drivers/wireguard_api.py +1 -1
  9. hiddifypanel/hutils/crypto.py +27 -0
  10. hiddifypanel/hutils/flask.py +5 -3
  11. hiddifypanel/hutils/network/cf_api.py +5 -5
  12. hiddifypanel/hutils/proxy/clash.py +1 -1
  13. hiddifypanel/hutils/proxy/shared.py +8 -12
  14. hiddifypanel/hutils/proxy/singbox.py +3 -1
  15. hiddifypanel/hutils/proxy/xray.py +1 -1
  16. hiddifypanel/models/admin.py +1 -1
  17. hiddifypanel/models/base_account.py +3 -0
  18. hiddifypanel/models/config.py +1 -1
  19. hiddifypanel/models/config_enum.py +13 -0
  20. hiddifypanel/models/user.py +2 -2
  21. hiddifypanel/panel/admin/AdminstratorAdmin.py +9 -2
  22. hiddifypanel/panel/admin/ProxyAdmin.py +4 -0
  23. hiddifypanel/panel/admin/QuickSetup.py +40 -9
  24. hiddifypanel/panel/admin/SettingAdmin.py +6 -0
  25. hiddifypanel/panel/admin/UserAdmin.py +10 -8
  26. hiddifypanel/panel/admin/adminlte.py +1 -1
  27. hiddifypanel/panel/cli.py +3 -1
  28. hiddifypanel/panel/commercial/restapi/v2/user/apps_api.py +76 -6
  29. hiddifypanel/panel/common.py +6 -3
  30. hiddifypanel/panel/common_bp/login.py +14 -8
  31. hiddifypanel/panel/init_db.py +139 -62
  32. hiddifypanel/panel/user/templates/base_singbox_config.json.j2 +16 -0
  33. hiddifypanel/panel/user/templates/home/home.html +1 -2
  34. hiddifypanel/panel/user/templates/home/multi.html +1 -2
  35. hiddifypanel/panel/user/user.py +1 -1
  36. hiddifypanel/static/apps-icon/singbox.ico +0 -0
  37. hiddifypanel/templates/fake.html +320 -0
  38. hiddifypanel/translations/en/LC_MESSAGES/messages.mo +0 -0
  39. hiddifypanel/translations/en/LC_MESSAGES/messages.po +50 -24
  40. hiddifypanel/translations/fa/LC_MESSAGES/messages.mo +0 -0
  41. hiddifypanel/translations/fa/LC_MESSAGES/messages.po +47 -25
  42. hiddifypanel/translations/pt/LC_MESSAGES/messages.mo +0 -0
  43. hiddifypanel/translations/pt/LC_MESSAGES/messages.po +41 -18
  44. hiddifypanel/translations/ru/LC_MESSAGES/messages.mo +0 -0
  45. hiddifypanel/translations/ru/LC_MESSAGES/messages.po +56 -29
  46. hiddifypanel/translations/zh/LC_MESSAGES/messages.mo +0 -0
  47. hiddifypanel/translations/zh/LC_MESSAGES/messages.po +34 -14
  48. hiddifypanel/translations.i18n/en.json +27 -11
  49. hiddifypanel/translations.i18n/fa.json +26 -10
  50. hiddifypanel/translations.i18n/fr.json +26 -8
  51. hiddifypanel/translations.i18n/my.json +1266 -0
  52. hiddifypanel/translations.i18n/pt.json +21 -5
  53. hiddifypanel/translations.i18n/ru.json +29 -13
  54. hiddifypanel/translations.i18n/zh.json +20 -4
  55. hiddifypanel-10.80.0.dev1.dist-info/METADATA +130 -0
  56. {hiddifypanel-10.70.9.dist-info → hiddifypanel-10.80.0.dev1.dist-info}/RECORD +111 -110
  57. {hiddifypanel-10.70.9.dist-info → hiddifypanel-10.80.0.dev1.dist-info}/WHEEL +1 -2
  58. hiddifypanel-10.80.0.dev1.dist-info/entry_points.txt +3 -0
  59. hiddifypanel-10.70.9.dist-info/METADATA +0 -144
  60. hiddifypanel-10.70.9.dist-info/entry_points.txt +0 -2
  61. hiddifypanel-10.70.9.dist-info/top_level.txt +0 -1
  62. {hiddifypanel-10.70.9.dist-info → hiddifypanel-10.80.0.dev1.dist-info}/LICENSE.md +0 -0
hiddifypanel/VERSION CHANGED
@@ -1 +1 @@
1
- 10.70.9
1
+ 10.80.0.dev7
hiddifypanel/VERSION.py CHANGED
@@ -1,3 +1,6 @@
1
- __version__='10.70.9'
1
+ import importlib.metadata
2
2
  from datetime import datetime
3
- __release_date__= datetime.strptime('2024-11-18','%Y-%m-%d')
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__, __release_date__
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
- from hiddifypanel.cache import redis_client
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
- app.config['SESSION_TYPE'] = 'redis'
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
- # csrf.protect()
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('unix:///opt/hiddify-manager/other/redis/run.sock?db=0')
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('unix:///opt/hiddify-manager/other/redis/run.sock?db=1', decode_responses=True)
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('unix:///opt/hiddify-manager/other/redis/run.sock?db=1')
16
+ self.redis_client = redis.from_url(os.environ.get("REDIS_URI_SSH",""))
17
17
 
18
18
  return self.redis_client
19
19
 
@@ -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
@@ -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) < 7:
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'] = 7
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 CloudFlare
1
+ import cloudflare
2
2
  from hiddifypanel.models import hconfig, ConfigEnum
3
3
 
4
- __cf: CloudFlare.CloudFlare = '' # type: ignore
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, CloudFlare.CloudFlare):
10
+ if __cf and isinstance(__cf, cloudflare.Cloudflare):
11
11
  return True
12
12
 
13
13
  if hconfig(ConfigEnum.cloudflare):
14
- __cf = CloudFlare.CloudFlare(token=hconfig(ConfigEnum.cloudflare))
15
- if __cf and isinstance(__cf, CloudFlare.CloudFlare):
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('host_key', [])
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
- key_files = glob.glob(current_app.config['HIDDIFY_CONFIG_PATH'] + "/other/ssh/host_key/*_key.pub")
16
- host_keys = []
17
- for file_name in key_files:
18
- with open(file_name, "r") as f:
19
- host_key = f.read().strip()
20
- host_key = host_key.split()
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['host_key'] = hutils.proxy.get_ssh_hostkeys(False)
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('host_key', [])
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["host_key"])
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
 
@@ -190,4 +190,4 @@ class AdminUser(BaseAccount, SerializerMixin):
190
190
  def before_insert(mapper, connection, target):
191
191
  from hiddifypanel import hutils
192
192
  hutils.model.gen_username(target)
193
- hutils.model.gen_password(target)
193
+ # hutils.model.gen_password(target)
@@ -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):
@@ -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(2048))
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
@@ -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=1000, nullable=False)
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: get_quick_setup_form,
32
- 3: get_proxy_form}
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 = "3"
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 = "2"
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):