66import json
77import queue
88import requests
9+ import secrets
10+ import string
911import threading
1012import time
1113import re
141143 "你的公网IP已泄露 请加群https://join.nanoda.work/#/join联系我们解除安全限制"
142144)
143145PUBLIC_WEBUI_WITHOUT_PASSWORD_MESSAGE = "当前配置允许所有设备访问,请添加密码\n \n 设置方法:\n 在config/deploy.yaml中添加:\n WebUI:\n Password: 你的密码\n 然后重启\n \n 温馨提示:密码推荐大小写字母+数字不小于六位\n \n 目前配置允許所有裝置存取,請新增密碼。\n \n 設定方法:\n 在config/deploy.yaml中添加:\n WebUI:\n Password: 你的密碼\n 然後重新啟動\n \n 溫馨提示:密碼建議包含大小寫英文字母與數字,且不少於六位。\n \n The current configuration allows access from all devices. Please set a password.\n \n How to configure:\n Add the following to config/deploy.yaml:\n WebUI:\n Password: your_password\n Then restart the application.\n \n Tip: It is recommended to use a password containing uppercase and lowercase letters as well as numbers, with a minimum length of 6 characters.\n \n 現在の設定では、すべてのデバイスからアクセスできます。パスワードを設定してください。\n \n 設定方法:\n config/deploy.yaml に以下を追加してください:\n WebUI:\n Password: あなたのパスワード\n その後、アプリケーションを再起動してください。\n \n ヒント:パスワードは英大文字・英小文字・数字を含み、6文字以上にすることを推奨します。"
146+ PUBLIC_WEBUI_PASSWORD_GENERATE_FAILED_MESSAGE = (
147+ "当前配置允许所有设备访问,但自动生成密码失败,请手动在 config/deploy.yaml 设置 Password 后重启。"
148+ )
149+ WEBUI_AUTO_PASSWORD_FILE = "password.txt"
144150
145151
146152def is_public_webui_host (host ):
@@ -170,6 +176,57 @@ def is_webui_password_set(password):
170176 return bool (str (password or "" ).strip ())
171177
172178
179+ def generate_webui_password (length = 32 ):
180+ """
181+ 生成包含大小写字母和数字的 WebUI 密码。
182+
183+ Args:
184+ length (int): 密码长度。
185+
186+ Returns:
187+ str: 随机密码。
188+ """
189+ letters_upper = string .ascii_uppercase
190+ letters_lower = string .ascii_lowercase
191+ digits = string .digits
192+ alphabet = letters_upper + letters_lower + digits
193+ password = [
194+ secrets .choice (letters_upper ),
195+ secrets .choice (letters_lower ),
196+ secrets .choice (digits ),
197+ ]
198+ password .extend (secrets .choice (alphabet ) for _ in range (length - len (password )))
199+ secrets .SystemRandom ().shuffle (password )
200+ return "" .join (password )
201+
202+
203+ def ensure_public_webui_password (key ):
204+ """
205+ 公网监听且未设置密码时自动生成密码。
206+
207+ Args:
208+ key: 命令行或部署配置中的 WebUI 密码。
209+
210+ Returns:
211+ tuple[str | None, str | None]: 有效密码和失败原因。
212+ """
213+ host = State .webui_host or State .deploy_config .WebuiHost
214+ if not is_public_webui_host (host ) or is_webui_password_set (key ):
215+ return key , None
216+
217+ try :
218+ password = generate_webui_password ()
219+ from deploy .atomic import atomic_write
220+
221+ atomic_write (WEBUI_AUTO_PASSWORD_FILE , f"{ password } \n " )
222+ State .deploy_config .Password = password
223+ logger .warning (f"WebUI 已自动生成密码,请在根目录 { WEBUI_AUTO_PASSWORD_FILE } 查看。" )
224+ return password , None
225+ except Exception as e :
226+ logger .exception (f"WebUI 自动生成密码失败: { e } " )
227+ return None , str (e )
228+
229+
173230def timedelta_to_text (delta = None ):
174231 time_delta_name_suffix_dict = {
175232 "Y" : "YearsAgo" ,
@@ -5126,6 +5183,7 @@ def app():
51265183 AlasGUI .set_theme (theme = theme )
51275184 lang .LANG = State .deploy_config .Language
51285185 key = args .key or State .deploy_config .Password
5186+ key , password_error = ensure_public_webui_password (key )
51295187 cdn = args .cdn if args .cdn else State .deploy_config .CDN
51305188 runs = None
51315189 if args .run :
@@ -5160,13 +5218,12 @@ def _block_restricted_device():
51605218 )
51615219 return True
51625220
5163- def _block_public_webui_without_password ():
5164- host = State .webui_host or State .deploy_config .WebuiHost
5165- if not is_public_webui_host (host ) or is_webui_password_set (key ):
5221+ def _block_public_webui_password_error ():
5222+ if password_error is None :
51665223 return False
51675224 popup (
51685225 "安全保护" ,
5169- PUBLIC_WEBUI_WITHOUT_PASSWORD_MESSAGE ,
5226+ PUBLIC_WEBUI_PASSWORD_GENERATE_FAILED_MESSAGE ,
51705227 implicit_close = False ,
51715228 closable = False ,
51725229 )
@@ -5175,9 +5232,9 @@ def _block_public_webui_without_password():
51755232 def index ():
51765233 if _block_restricted_device ():
51775234 return
5178- if _block_public_webui_without_password ():
5235+ if _block_public_webui_password_error ():
51795236 return
5180- if key is not None and not login (key ):
5237+ if is_webui_password_set ( key ) and not login (key ):
51815238 logger .warning (f"{ info .user_ip } login failed." )
51825239 time .sleep (1.5 )
51835240 run_js ("location.reload();" )
@@ -5189,9 +5246,9 @@ def index():
51895246 def manage ():
51905247 if _block_restricted_device ():
51915248 return
5192- if _block_public_webui_without_password ():
5249+ if _block_public_webui_password_error ():
51935250 return
5194- if key is not None and not login (key ):
5251+ if is_webui_password_set ( key ) and not login (key ):
51955252 logger .warning (f"{ info .user_ip } login failed." )
51965253 time .sleep (1.5 )
51975254 run_js ("location.reload();" )
0 commit comments