6d4e2b56 by 柴进

增加登陆日志留存模块

1 parent af04bd07
...@@ -27,6 +27,8 @@ from google import genai ...@@ -27,6 +27,8 @@ from google import genai
27 from google.genai import types 27 from google.genai import types
28 import hashlib 28 import hashlib
29 import pymysql 29 import pymysql
30 import socket
31 import requests
30 from datetime import datetime 32 from datetime import datetime
31 33
32 34
...@@ -117,10 +119,12 @@ class LoginDialog(QDialog): ...@@ -117,10 +119,12 @@ class LoginDialog(QDialog):
117 # Username field 119 # Username field
118 username_label = QLabel("用户名") 120 username_label = QLabel("用户名")
119 username_label.setObjectName("field_label") 121 username_label.setObjectName("field_label")
122
120 main_layout.addWidget(username_label) 123 main_layout.addWidget(username_label)
121 124
122 self.username_entry = QLineEdit() 125 self.username_entry = QLineEdit()
123 self.username_entry.setText(self.last_user) 126 self.username_entry.setText(self.last_user)
127 self.username_entry.setFixedHeight(40)
124 main_layout.addWidget(self.username_entry) 128 main_layout.addWidget(self.username_entry)
125 129
126 main_layout.addSpacing(10) 130 main_layout.addSpacing(10)
...@@ -132,6 +136,7 @@ class LoginDialog(QDialog): ...@@ -132,6 +136,7 @@ class LoginDialog(QDialog):
132 136
133 self.password_entry = QLineEdit() 137 self.password_entry = QLineEdit()
134 self.password_entry.setEchoMode(QLineEdit.Password) 138 self.password_entry.setEchoMode(QLineEdit.Password)
139 self.password_entry.setFixedHeight(40)
135 140
136 # Handle saved password placeholder 141 # Handle saved password placeholder
137 if self.saved_password_hash: 142 if self.saved_password_hash:
...@@ -159,6 +164,7 @@ class LoginDialog(QDialog): ...@@ -159,6 +164,7 @@ class LoginDialog(QDialog):
159 # Login button 164 # Login button
160 self.login_button = QPushButton("登录") 165 self.login_button = QPushButton("登录")
161 self.login_button.setObjectName("login_button") 166 self.login_button.setObjectName("login_button")
167 self.login_button.setFixedHeight(40)
162 self.login_button.clicked.connect(self.on_login) 168 self.login_button.clicked.connect(self.on_login)
163 main_layout.addWidget(self.login_button) 169 main_layout.addWidget(self.login_button)
164 170
...@@ -287,9 +293,13 @@ class LoginDialog(QDialog): ...@@ -287,9 +293,13 @@ class LoginDialog(QDialog):
287 self.success = True 293 self.success = True
288 self.authenticated_user = username 294 self.authenticated_user = username
289 self.current_password_hash = password_hash 295 self.current_password_hash = password_hash
296
297 # 记录用户登录日志
298 self.log_user_login(username)
299
290 self.accept() # Close dialog with success 300 self.accept() # Close dialog with success
291 else: 301 else:
292 self.show_error("用户名或密码错误") 302 self.show_error("用户名或密码错误,联系[柴进]重置密码")
293 self.password_entry.clear() 303 self.password_entry.clear()
294 self.password_changed = False 304 self.password_changed = False
295 self.login_button.setEnabled(True) 305 self.login_button.setEnabled(True)
...@@ -303,8 +313,96 @@ class LoginDialog(QDialog): ...@@ -303,8 +313,96 @@ class LoginDialog(QDialog):
303 self.show_error(f"认证失败: {str(e)}") 313 self.show_error(f"认证失败: {str(e)}")
304 self.login_button.setEnabled(True) 314 self.login_button.setEnabled(True)
305 315
316 def get_local_ip(self):
317 """获取局域网IP地址"""
318 try:
319 # 尝试获取真实的局域网IP,而不是127.0.0.1
320 hostname = socket.gethostname()
321 local_ip = socket.gethostbyname(hostname)
322
323 # 如果获取到的是127.0.0.1,尝试连接外部地址获取本地IP
324 if local_ip.startswith('127.'):
325 # 创建一个UDP socket连接到公共DNS服务器
326 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
327 try:
328 s.connect(('8.8.8.8', 80))
329 local_ip = s.getsockname()[0]
330 except:
331 pass
332 finally:
333 s.close()
334
335 return local_ip
336 except:
337 return "127.0.0.1"
338
339 def get_public_ip(self):
340 """获取公网IP地址,失败返回None"""
341 try:
342 # 使用多个API作为备选
343 apis = [
344 'https://api.ipify.org',
345 'https://ifconfig.me',
346 'https://ipinfo.io/ip'
347 ]
348
349 for api in apis:
350 try:
351 response = requests.get(api, timeout=3)
352 if response.status_code == 200:
353 public_ip = response.text.strip()
354 # 简单验证IP格式
355 if len(public_ip.split('.')) == 4 or ':' in public_ip:
356 return public_ip
357 except:
358 continue
359 return None
360 except:
361 return None
362
363 def get_device_name(self):
364 """获取设备名称"""
365 try:
366 return socket.gethostname()
367 except:
368 return "Unknown"
369
370 def log_user_login(self, username):
371 """静默记录用户登录日志,支持双IP"""
372 try:
373 local_ip = self.get_local_ip()
374 public_ip = self.get_public_ip()
375 device_name = self.get_device_name()
376
377 conn = pymysql.connect(
378 host=self.db_config['host'],
379 port=self.db_config.get('port', 3306),
380 user=self.db_config['user'],
381 password=self.db_config['password'],
382 database=self.db_config['database'],
383 connect_timeout=5
384 )
385
386 try:
387 with conn.cursor() as cursor:
388 sql = """INSERT INTO nano_banana_user_log
389 (user_name, local_ip, public_ip, device_name, login_time)
390 VALUES (%s, %s, %s, %s, %s)"""
391 cursor.execute(sql, (username, local_ip, public_ip, device_name, datetime.now()))
392 conn.commit()
393 finally:
394 conn.close()
395
396 except Exception:
397 # 静默处理,不影响登录流程
398 pass
399
306 def show_error(self, message): 400 def show_error(self, message):
307 """Display error message""" 401 """显示错误弹窗和标签"""
402 # 显示弹窗错误提示
403 QMessageBox.critical(self, "登录错误", message)
404
405 # 同时保留标签显示
308 self.error_label.setText(message) 406 self.error_label.setText(message)
309 self.error_label.setStyleSheet("QLabel { color: #ff3b30; }") 407 self.error_label.setStyleSheet("QLabel { color: #ff3b30; }")
310 408
......