添加解绑功能

This commit is contained in:
wsb1224 2025-11-28 15:56:33 +08:00
parent 74405da203
commit 9f260f3f29
11 changed files with 777 additions and 733 deletions

View File

@ -9,11 +9,19 @@ from . import api_bp
def verify_license():
"""验证卡密接口"""
try:
data = request.get_json()
# 检查Content-Type
content_type = request.content_type
if content_type and 'application/json' not in content_type:
current_app.logger.warning(f"验证请求Content-Type不正确 - {content_type}")
data = request.get_json(force=True) # 强制解析JSON即使Content-Type不正确
if not data:
# 尝试获取原始数据用于调试
raw_data = request.get_data(as_text=True)
current_app.logger.warning(f"验证请求:请求数据为空或无法解析 - Content-Type: {content_type}, 原始数据: {raw_data[:200] if raw_data else 'None'}")
return jsonify({
'success': False,
'message': '请求数据为空'
'message': '请求数据为空或格式错误'
}), 400
# 获取请求参数
@ -23,27 +31,47 @@ def verify_license():
timestamp = data.get('timestamp')
signature = data.get('signature')
# 验证必填参数
if not all([software_id, license_key, machine_code, timestamp, signature]):
# 验证必填参数,并记录缺失的参数
missing_params = []
if not software_id:
missing_params.append('software_id')
if not license_key:
missing_params.append('license_key')
if not machine_code:
missing_params.append('machine_code')
if timestamp is None:
missing_params.append('timestamp')
if not signature:
missing_params.append('signature')
if missing_params:
current_app.logger.warning(f"验证请求:缺少必要参数 - {', '.join(missing_params)}")
return jsonify({
'success': False,
'message': '缺少必要参数'
'message': f'缺少必要参数: {", ".join(missing_params)}'
}), 400
# 验证时间戳(防止重放攻击)
try:
request_time = datetime.fromtimestamp(int(timestamp))
# 增加时间验证的宽容度到5分钟300秒
time_diff = abs((datetime.utcnow() - request_time).total_seconds())
# 确保timestamp是数字类型
if isinstance(timestamp, str):
timestamp = int(timestamp)
# 使用utcfromtimestamp确保时间戳被解析为UTC时间与time.time()生成的UTC时间戳一致
request_time = datetime.utcfromtimestamp(timestamp)
# 使用UTC时间进行比较
current_time = datetime.utcnow()
time_diff = abs((current_time - request_time).total_seconds())
if time_diff > 300: # 5分钟有效期
current_app.logger.warning(f"验证请求:请求已过期 - 时间差: {time_diff}秒, 当前时间: {current_time}, 请求时间: {request_time}")
return jsonify({
'success': False,
'message': '请求已过期'
}), 400
except (ValueError, TypeError):
except (ValueError, TypeError) as e:
current_app.logger.warning(f"验证请求:时间戳格式错误 - timestamp: {timestamp}, 错误: {str(e)}")
return jsonify({
'success': False,
'message': '时间戳格式错误'
'message': f'时间戳格式错误: {str(e)}'
}), 400
# 验证签名
@ -51,6 +79,9 @@ def verify_license():
signature_data = f"{software_id}{license_key}{machine_code}{timestamp}"
expected_signature = generate_signature(signature_data, secret_key)
# 记录请求信息(不记录敏感信息)
current_app.logger.info(f"验证请求 - software_id: {software_id}, machine_code: {machine_code[:8]}..., timestamp: {timestamp}")
# 调试信息(仅在调试模式记录,且不输出密钥)
if current_app.debug:
current_app.logger.debug(f"签名数据: {signature_data}")
@ -58,6 +89,7 @@ def verify_license():
current_app.logger.debug(f"服务端签名匹配: {signature == expected_signature}")
if signature != expected_signature:
current_app.logger.warning(f"验证请求:签名验证失败 - software_id: {software_id}")
return jsonify({
'success': False,
'message': '签名验证失败'
@ -120,7 +152,7 @@ def verify_license():
latest_version = Version.query.filter_by(
product_id=software_id,
publish_status=1
).order_by(Version.create_time.desc()).first()
).order_by(Version.update_time.desc(), Version.create_time.desc()).first()
response_data = {
'license_key': license_obj.license_key,
@ -150,11 +182,36 @@ def verify_license():
})
except Exception as e:
current_app.logger.error(f"卡密验证失败: {str(e)}")
return jsonify({
'success': False,
'message': '服务器内部错误'
}), 500
# 记录详细的错误信息,包括堆栈跟踪
import traceback
error_trace = traceback.format_exc()
current_app.logger.error(f"卡密验证失败: {str(e)}\n{error_trace}")
# 尝试回滚数据库事务
try:
db.session.rollback()
except:
pass
# 检查是否是数据库连接错误
error_str = str(e).lower()
error_type = type(e).__name__
if 'operationalerror' in error_str or 'connection' in error_str or 'database' in error_str or 'OperationalError' in error_type:
return jsonify({
'success': False,
'message': '数据库连接失败,请稍后重试'
}), 503
elif 'timeout' in error_str or 'Timeout' in error_type:
return jsonify({
'success': False,
'message': '请求处理超时,请稍后重试'
}), 503
else:
return jsonify({
'success': False,
'message': '服务器内部错误'
}), 500
@api_bp.route('/auth/activate', methods=['POST'])
def activate_license():
@ -309,3 +366,86 @@ def heartbeat():
except Exception as e:
current_app.logger.error(f"心跳处理失败: {str(e)}")
return jsonify({'success': False}), 500
@api_bp.route('/auth/unbind', methods=['POST'])
def unbind_license():
"""用户端解绑卡密接口"""
try:
data = request.get_json()
if not data:
return jsonify({
'success': False,
'message': '请求数据为空'
}), 400
software_id = data.get('software_id')
license_key = data.get('license_key')
machine_code = data.get('machine_code')
timestamp = data.get('timestamp')
signature = data.get('signature')
# 验证必填参数
if not all([software_id, license_key, machine_code, timestamp, signature]):
return jsonify({
'success': False,
'message': '缺少必要参数'
}), 400
# 验证签名
secret_key = current_app.config.get('AUTH_SECRET_KEY', 'default-secret-key')
signature_data = f"{software_id}{license_key}{machine_code}{timestamp}"
expected_signature = generate_signature(signature_data, secret_key)
if signature != expected_signature:
return jsonify({
'success': False,
'message': '签名验证失败'
}), 401
# 查找产品
product = Product.query.filter_by(product_id=software_id).first()
if not product:
return jsonify({
'success': False,
'message': '产品不存在'
}), 404
# 查找卡密
license_obj = License.query.filter_by(
license_key=license_key,
product_id=software_id
).first()
if not license_obj:
return jsonify({
'success': False,
'message': '卡密不存在'
}), 404
# 检查卡密是否绑定到当前机器码
if license_obj.bind_machine_code != machine_code:
return jsonify({
'success': False,
'message': '卡密未绑定到当前设备'
}), 400
# 执行解绑操作
success, message = license_obj.unbind()
if not success:
return jsonify({
'success': False,
'message': message
}), 400
return jsonify({
'success': True,
'message': '解绑成功'
})
except Exception as e:
current_app.logger.error(f"卡密解绑失败: {str(e)}")
return jsonify({
'success': False,
'message': '服务器内部错误'
}), 500

View File

@ -100,11 +100,12 @@ def get_products():
version_stats = db.session.query(
Version.product_id,
Version.version_num,
Version.update_time,
Version.create_time
).filter(
Version.product_id.in_(product_ids),
Version.publish_status == 1
).order_by(Version.create_time.desc()).all()
).order_by(Version.update_time.desc(), Version.create_time.desc()).all()
version_dict = {}
for v in version_stats:

View File

@ -57,7 +57,7 @@ def get_user_products():
latest_version = Version.query.filter_by(
product_id=product.product_id,
publish_status=1
).order_by(Version.create_time.desc()).first()
).order_by(Version.update_time.desc(), Version.create_time.desc()).first()
product_dict['latest_version'] = latest_version.version_num if latest_version else None
# 移除不存在的is_paid字段
@ -103,7 +103,7 @@ def get_user_product(product_id):
latest_version = Version.query.filter_by(
product_id=product_id,
publish_status=1
).order_by(Version.create_time.desc()).first()
).order_by(Version.update_time.desc(), Version.create_time.desc()).first()
if latest_version:
product_dict['latest_version'] = latest_version.version_num
@ -112,7 +112,7 @@ def get_user_product(product_id):
recent_versions = Version.query.filter_by(
product_id=product_id,
publish_status=1
).order_by(Version.create_time.desc()).limit(3).all()
).order_by(Version.update_time.desc(), Version.create_time.desc()).limit(3).all()
product_dict['recent_updates'] = [
{
@ -237,7 +237,7 @@ def user_verify_license():
latest_version = Version.query.filter_by(
product_id=product_id,
publish_status=1
).order_by(Version.create_time.desc()).first()
).order_by(Version.update_time.desc(), Version.create_time.desc()).first()
return jsonify({
'success': True,
@ -564,7 +564,7 @@ def check_download_permission():
latest_version = Version.query.filter_by(
product_id=product_id,
publish_status=1
).order_by(Version.create_time.desc()).first()
).order_by(Version.update_time.desc(), Version.create_time.desc()).first()
return jsonify({
'success': True,

View File

@ -50,7 +50,7 @@ class Product(db.Model):
def get_latest_version(self):
"""获取最新版本"""
latest_version = self.versions.filter_by(publish_status=1).order_by(
db.desc(Version.create_time)
db.desc(Version.update_time), db.desc(Version.create_time)
).first()
return latest_version.version_num if latest_version else None

View File

@ -1,3 +0,0 @@
{
"remember_DEMO_SOFTWARE_2024": true
}

View File

@ -1 +0,0 @@
843665BD5F1FC5FE8946763877F8E457

View File

@ -1 +0,0 @@
E6684431683D6EAB7ED7FA9CE6C8BB9B

View File

@ -2,19 +2,26 @@
"""
Python软件授权验证器 (现代化UI版)
功能
1. 支持在线/离线验证
1. 在线验证每次启动都进行服务器验证
2. 自动保存/读取历史卡密
3. 现代化深色主题 UI
4. 机器码一键复制
5. 防止后台禁用卡密后仍能使用
使用方法 (完全兼容旧版):
from auth_validator import AuthValidator
validator = AuthValidator(software_id="your_software_id")
validator = AuthValidator(
software_id="your_software_id",
api_url="http://your-server.com/api/v1",
secret_key="your_secret_key"
)
if not validator.validate():
exit()
sys.exit()
"""
import sys # 加在文件开头,比如其他 import 语句后面
import os
import json
import time
@ -26,6 +33,16 @@ import uuid
from datetime import datetime, timedelta
from typing import Optional, Tuple, Dict, Any
# PyInstaller打包环境下的SSL证书处理
# 尝试导入certifi来解决打包后SSL证书问题
try:
import certifi
# 设置requests使用certifi的证书
os.environ['REQUESTS_CA_BUNDLE'] = certifi.where()
os.environ['SSL_CERT_FILE'] = certifi.where()
except ImportError:
pass
# 尝试导入现代化UI库如果未安装则提示
try:
import customtkinter as ctk
@ -33,7 +50,7 @@ try:
from tkinter import messagebox
except ImportError:
print("错误: 请先安装UI库 -> pip install customtkinter")
exit(1)
sys.exit(1)
# ==========================================
@ -131,68 +148,153 @@ class AuthCore:
self.machine_code = MachineCodeGenerator.get()
self.token_file = f".auth_{software_id}.token"
def check_local_cache(self) -> bool:
"""检查本地缓存是否有效(静默验证)"""
if not os.path.exists(self.token_file):
return False
def test_connection(self) -> Tuple[bool, str]:
"""测试服务器连接"""
try:
with open(self.token_file, 'r') as f:
data = json.load(f)
# 尝试访问一个简单的端点(如果存在)或直接测试连接
test_url = f"{self.api_url}/auth/verify"
# 发送一个简单的HEAD请求测试连接如果服务器支持
# 否则发送一个最小化的POST请求
test_data = {
"software_id": self.software_id,
"license_key": "TEST",
"machine_code": self.machine_code,
"timestamp": int(time.time()),
"signature": "test"
}
resp = requests.post(test_url, json=test_data, timeout=3)
# 即使返回错误,只要不是连接错误,说明服务器可达
return True, "服务器连接正常"
except requests.exceptions.Timeout:
return False, f"连接超时,服务器可能无响应: {self.api_url}"
except requests.exceptions.ConnectionError as e:
error_detail = str(e)
if "Name or service not known" in error_detail:
return False, f"无法解析服务器地址: {self.api_url}"
elif "Connection refused" in error_detail:
return False, f"服务器拒绝连接,请确认服务器是否运行: {self.api_url}"
else:
return False, f"无法连接到服务器: {self.api_url}"
except Exception as e:
return False, f"连接测试失败: {str(e)}"
# 校验机器码
if data.get('machine_code') != self.machine_code:
return False
# 校验过期时间
expire_str = data.get('expire_time')
if expire_str and expire_str != "永久":
expire_time = datetime.fromisoformat(expire_str)
if datetime.utcnow() > expire_time:
return False
# 校验本地缓存时效 (例如每7天必须联网一次)
last_check = datetime.fromisoformat(data.get('last_check', '2000-01-01'))
if datetime.utcnow() - last_check > timedelta(days=7):
return False
return True
def clear_token(self):
"""清除本地Token缓存"""
try:
if os.path.exists(self.token_file):
os.remove(self.token_file)
except:
return False
pass
def verify_online(self, license_key: str) -> Tuple[bool, str, dict]:
"""在线验证"""
try:
# 这里模拟网络请求,请替换为真实的 request.post
# 真实代码示例:
"""
# 生成时间戳
timestamp = int(time.time())
sign = hashlib.sha256(f"{self.software_id}{license_key}{self.machine_code}{timestamp}{self.secret_key}".encode()).hexdigest()
resp = requests.post(f"{self.api_url}/verify", json={...}, timeout=self.timeout)
result = resp.json()
"""
# === 模拟后端返回 (仅供测试请根据实际API修改) ===
import time
time.sleep(0.8) # 模拟网络延迟
# 生成签名数据
signature_data = f"{self.software_id}{license_key}{self.machine_code}{timestamp}"
# 模拟: 只要输入不为空且不含 'FAIL' 就算成功
if not license_key or "FAIL" in license_key.upper():
return False, "无效的卡密或订阅已过期", {}
# 生成签名
combined = f"{signature_data}{self.secret_key}".encode('utf-8')
signature = hashlib.sha256(combined).hexdigest()
fake_response = {
"success": True,
"msg": "验证成功",
"data": {
"expire_time": (datetime.utcnow() + timedelta(days=30)).isoformat(),
"machine_code": self.machine_code,
"last_check": datetime.utcnow().isoformat()
}
# 构建请求数据
request_data = {
"software_id": self.software_id,
"license_key": license_key,
"machine_code": self.machine_code,
"timestamp": timestamp,
"signature": signature,
"software_version": "1.0.0" # 可以后续从配置中读取
}
return True, "验证成功", fake_response["data"]
# ============================================
# 发送POST请求
verify_url = f"{self.api_url}/auth/verify"
# 添加调试信息
debug_info = f"请求URL: {verify_url}\n请求数据: {request_data}"
try:
# 在PyInstaller打包环境中可能需要特殊处理SSL验证
# 如果是打包环境尝试禁用SSL验证仅用于测试
is_frozen = getattr(sys, 'frozen', False)
if is_frozen:
# 打包环境,可能需要特殊处理
resp = requests.post(verify_url, json=request_data, timeout=self.timeout, verify=False)
else:
resp = requests.post(verify_url, json=request_data, timeout=self.timeout)
except requests.exceptions.Timeout:
return False, f"连接超时({self.timeout}秒),请检查网络连接或服务器地址: {self.api_url}", {}
except requests.exceptions.ConnectionError as e:
# 提供更详细的连接错误信息
error_detail = str(e)
if "Name or service not known" in error_detail or "nodename nor servname provided" in error_detail:
return False, f"无法解析服务器地址请检查API地址是否正确: {self.api_url}", {}
elif "Connection refused" in error_detail:
return False, f"服务器拒绝连接,请确认服务器是否运行在: {self.api_url}", {}
elif "No route to host" in error_detail:
return False, f"无法到达服务器,请检查网络连接: {self.api_url}", {}
else:
return False, f"无法连接到服务器 ({self.api_url}),请检查网络连接和服务器状态\n详细错误: {error_detail}", {}
except Exception as e:
return False, f"网络请求异常: {str(e)}\n{debug_info}", {}
# 检查HTTP状态码
if resp.status_code != 200:
# 处理特定的HTTP状态码
if resp.status_code == 503:
return False, "服务器暂时不可用,请稍后重试", {}
elif resp.status_code == 500:
return False, "服务器内部错误,请联系管理员", {}
elif resp.status_code == 404:
return False, "API接口不存在请检查API地址", {}
elif resp.status_code == 401:
return False, "签名验证失败,请检查密钥配置", {}
else:
try:
error_data = resp.json()
error_msg = error_data.get('message', f'服务器返回错误: {resp.status_code}')
except:
error_msg = f'服务器返回错误: {resp.status_code}'
return False, error_msg, {}
# 解析响应
try:
result = resp.json()
except Exception as e:
return False, f"服务器响应格式错误: {str(e)}\n响应内容: {resp.text}", {}
# 检查验证结果
if not result.get('success', False):
error_msg = result.get('message', '验证失败')
return False, error_msg, {}
# 验证成功,提取数据
data = result.get('data', {})
# 构建返回数据(兼容原有格式)
response_data = {
"expire_time": data.get('expire_time'),
"machine_code": self.machine_code,
"last_check": datetime.utcnow().isoformat(),
"license_key": data.get('license_key', license_key),
"type": data.get('type'),
"type_name": data.get('type_name', ''),
"remaining_days": data.get('remaining_days'),
"product_name": data.get('product_name', '')
}
return True, result.get('message', '验证成功'), response_data
except requests.exceptions.Timeout:
return False, "连接超时,请检查网络连接", {}
except requests.exceptions.ConnectionError as e:
return False, f"无法连接到服务器: {str(e)},请检查网络连接和服务器地址", {}
except requests.exceptions.RequestException as e:
return False, f"网络请求失败: {str(e)}", {}
except Exception as e:
return False, f"网络连接失败: {str(e)}", {}
return False, f"验证过程出错: {str(e)}", {}
def save_token(self, data: dict):
"""验证成功后保存Token"""
@ -202,6 +304,96 @@ class AuthCore:
except:
pass
def unbind_license(self, license_key: str) -> Tuple[bool, str]:
"""解绑卡密与机器码的绑定"""
try:
# 生成时间戳
timestamp = int(time.time())
# 生成签名数据
signature_data = f"{self.software_id}{license_key}{self.machine_code}{timestamp}"
# 生成签名
combined = f"{signature_data}{self.secret_key}".encode('utf-8')
signature = hashlib.sha256(combined).hexdigest()
# 构建请求数据
request_data = {
"software_id": self.software_id,
"license_key": license_key,
"machine_code": self.machine_code,
"timestamp": timestamp,
"signature": signature
}
# 发送POST请求到解绑接口
unbind_url = f"{self.api_url}/auth/unbind"
try:
# 在PyInstaller打包环境中可能需要特殊处理SSL验证
is_frozen = getattr(sys, 'frozen', False)
if is_frozen:
# 打包环境,可能需要特殊处理
resp = requests.post(unbind_url, json=request_data, timeout=self.timeout, verify=False)
else:
resp = requests.post(unbind_url, json=request_data, timeout=self.timeout)
except requests.exceptions.Timeout:
return False, f"连接超时({self.timeout}秒),请检查网络连接或服务器地址: {self.api_url}"
except requests.exceptions.ConnectionError as e:
# 提供更详细的连接错误信息
error_detail = str(e)
if "Name or service not known" in error_detail or "nodename nor servname provided" in error_detail:
return False, f"无法解析服务器地址请检查API地址是否正确: {self.api_url}"
elif "Connection refused" in error_detail:
return False, f"服务器拒绝连接,请确认服务器是否运行在: {self.api_url}"
elif "No route to host" in error_detail:
return False, f"无法到达服务器,请检查网络连接: {self.api_url}"
else:
return False, f"无法连接到服务器 ({self.api_url}),请检查网络连接和服务器状态\n详细错误: {error_detail}"
except Exception as e:
return False, f"网络请求异常: {str(e)}"
# 检查HTTP状态码
if resp.status_code != 200:
# 处理特定的HTTP状态码
if resp.status_code == 503:
return False, "服务器暂时不可用,请稍后重试"
elif resp.status_code == 500:
return False, "服务器内部错误,请联系管理员"
elif resp.status_code == 404:
return False, "API接口不存在请检查API地址"
elif resp.status_code == 401:
return False, "签名验证失败,请检查密钥配置"
else:
try:
error_data = resp.json()
error_msg = error_data.get('message', f'服务器返回错误: {resp.status_code}')
except:
error_msg = f'服务器返回错误: {resp.status_code}'
return False, error_msg
# 解析响应
try:
result = resp.json()
except Exception as e:
return False, f"服务器响应格式错误: {str(e)}\n响应内容: {resp.text}"
# 检查解绑结果
if not result.get('success', False):
error_msg = result.get('message', '解绑失败')
return False, error_msg
return True, result.get('message', '解绑成功')
except requests.exceptions.Timeout:
return False, "连接超时,请检查网络连接"
except requests.exceptions.ConnectionError as e:
return False, f"无法连接到服务器: {str(e)},请检查网络连接和服务器地址"
except requests.exceptions.RequestException as e:
return False, f"网络请求失败: {str(e)}"
except Exception as e:
return False, f"解绑过程出错: {str(e)}"
# ==========================================
# 3. 现代化 UI 层 (View)
@ -214,21 +406,52 @@ class AuthWindow(ctk.CTk):
super().__init__()
self.auth_core = auth_core
self.is_verified = False # 验证结果状态
self.auto_verify = False # 是否自动验证标志
self.is_destroyed = False # 窗口是否已销毁标志
self.pending_callbacks = [] # 待执行的after回调ID列表
self.verifying = False # 是否正在验证中
# 窗口基础设置
self.title("软件授权验证")
self.geometry("420x550")
self.resizable(False, False)
self.title("软件授权验证有问题联系Vtaiyi1224)")
self.geometry("400x550") # 增加高度以容纳新按钮
self.minsize(300, 450) # 更新最小窗口大小
self.resizable(True, True) # 启用窗口大小调整
ctk.set_appearance_mode("Dark")
ctk.set_default_color_theme("blue")
# 绑定窗口关闭事件
self.protocol("WM_DELETE_WINDOW", self._on_closing)
# 绑定窗口大小变化事件
self.bind("<Configure>", self._on_window_resize)
# 初始化布局参数
self.window_width = 400
self.window_height = 550
# 居中显示
self._center_window()
self._setup_ui()
# 自动填入上次卡密
# 自动填入上次卡密,如果有则自动验证
self._load_history()
def _on_closing(self):
"""窗口关闭时的处理用户点击X关闭"""
# 如果用户手动关闭窗口,且验证未完成或正在验证中,则视为验证失败
if self.verifying or not self.is_verified:
self.is_verified = False
self.is_destroyed = True
self.verifying = False
# 取消所有pending的after回调
for callback_id in self.pending_callbacks:
try:
self.after_cancel(callback_id)
except:
pass
self.pending_callbacks.clear()
self.destroy()
def _center_window(self):
self.update_idletasks()
width = self.winfo_width()
@ -237,13 +460,57 @@ class AuthWindow(ctk.CTk):
y = (self.winfo_screenheight() // 2) - (height // 2)
self.geometry(f'{width}x{height}+{x}+{y}')
def _on_window_resize(self, event):
"""窗口大小变化事件处理"""
# 只有当窗口确实是当前窗口且大小发生变化时才处理
if event.widget == self and (self.window_width != event.width or self.window_height != event.height):
self.window_width = event.width
self.window_height = event.height
self._update_layout()
def _update_layout(self):
"""动态更新布局"""
# 更新各组件的尺寸和位置
padding_x = max(20, int(self.window_width * 0.05))
# 更新头部区域
self.header.pack_configure(pady=(max(20, int(self.window_height * 0.04)), max(10, int(self.window_height * 0.02))))
# 动态调整标题字体大小
title_font_size = max(16, min(22, int(self.window_width * 0.04)))
self.title_label.configure(font=("Microsoft YaHei UI", title_font_size, "bold"))
# 更新机器码区域
self.mc_frame.pack_configure(padx=padding_x, pady=max(5, int(self.window_height * 0.01)))
# 更新输入区域
self.input_frame.pack_configure(padx=padding_x, pady=max(5, int(self.window_height * 0.01)))
# 更新服务器地址标签
self.lbl_server.pack_configure(pady=(max(5, int(self.window_height * 0.01)), 0))
# 更新状态标签
self.lbl_status.pack_configure(pady=(max(5, int(self.window_height * 0.01)), max(2, int(self.window_height * 0.005))))
# 更新验证按钮
self.btn_verify.pack_configure(padx=padding_x, pady=max(10, int(self.window_height * 0.02)))
# 动态调整状态标签的换行宽度
wrap_length = max(200, int(self.window_width * 0.8))
self.lbl_status.configure(wraplength=wrap_length)
# 动态调整底部标签的位置
self.footer_label.pack_configure(pady=max(5, int(self.window_height * 0.01)))
def _setup_ui(self):
# 1. 头部图标与标题
self.header = ctk.CTkFrame(self, fg_color="transparent")
self.header.pack(pady=(40, 20))
ctk.CTkLabel(self.header, text="🔐", font=("Segoe UI Emoji", 56)).pack()
ctk.CTkLabel(self.header, text="用户授权系统", font=("Microsoft YaHei UI", 22, "bold")).pack(pady=5)
self.icon_label = ctk.CTkLabel(self.header, text="🔐", font=("Segoe UI Emoji", 56))
self.icon_label.pack()
self.title_label = ctk.CTkLabel(self.header, text="用户授权系统", font=("Microsoft YaHei UI", 22, "bold"))
self.title_label.pack(pady=5)
# 2. 机器码显示区
self.mc_frame = ctk.CTkFrame(self, fg_color="#2B2B2B", corner_radius=8)
@ -279,36 +546,90 @@ class AuthWindow(ctk.CTk):
)
self.entry_key.pack(fill="x")
# 4. 状态提示信息
self.lbl_status = ctk.CTkLabel(self, text="等待验证...", text_color="gray", font=("Microsoft YaHei UI", 12))
self.lbl_status.pack(pady=(20, 5))
# 4. 操作按钮区
self.button_frame = ctk.CTkFrame(self, fg_color="transparent")
self.button_frame.pack(padx=30, pady=10, fill="x")
# 5. 验证按钮
# 验证按钮
self.btn_verify = ctk.CTkButton(
self,
self.button_frame,
text="立即验证授权",
height=50,
font=("Microsoft YaHei UI", 16, "bold"),
height=40,
font=("Microsoft YaHei UI", 14, "bold"),
command=self._handle_verify
)
self.btn_verify.pack(padx=30, pady=20, fill="x")
self.btn_verify.pack(fill="x", pady=(0, 10))
# 解绑按钮
self.btn_unbind = ctk.CTkButton(
self.button_frame,
text="解绑当前卡密",
height=40,
font=("Microsoft YaHei UI", 14),
fg_color="transparent",
border_width=2,
command=self._handle_unbind
)
self.btn_unbind.pack(fill="x")
# 5. 服务器地址显示(小字,灰色)
self.lbl_server = ctk.CTkLabel(
self,
text=f"服务器: {self.auth_core.api_url}",
text_color="#666",
font=("Microsoft YaHei UI", 9)
)
self.lbl_server.pack(pady=(10, 0))
# 6. 状态提示信息(支持多行)
self.lbl_status = ctk.CTkLabel(
self,
text="等待验证...",
text_color="gray",
font=("Microsoft YaHei UI", 12),
wraplength=360, # 允许自动换行
justify="left"
)
self.lbl_status.pack(pady=(10, 5))
# 底部版权
ctk.CTkLabel(self, text="Powered by AuthValidator", font=("Arial", 10), text_color="#444").pack(side="bottom",
pady=10)
self.footer_label = ctk.CTkLabel(self, text="Powered by AuthValidator", font=("Arial", 10), text_color="#444")
self.footer_label.pack(side="bottom", pady=10)
def _load_history(self):
"""读取历史卡密"""
"""读取历史卡密,如果有则自动验证"""
last_key = ConfigManager.get_last_key()
if last_key:
self.entry_key.insert(0, last_key)
self.lbl_status.configure(text="已自动填入上次卡密,请点击验证", text_color="#888")
self.lbl_status.configure(text="已自动填入上次卡密,正在验证中...", text_color="#2196F3")
# 延迟100ms后自动触发验证确保UI已完全加载
callback_id = self.after(100, self._safe_auto_verify)
self.pending_callbacks.append(callback_id)
else:
self.lbl_status.configure(text="请输入卡密并点击验证", text_color="gray")
def _safe_auto_verify(self):
"""安全地自动验证保存的卡密"""
if self.is_destroyed:
return
key = self.entry_key.get().strip()
if key:
self.auto_verify = True
self._handle_verify()
def _copy_machine_code(self):
if self.is_destroyed:
return
self.clipboard_clear()
self.clipboard_append(self.auth_core.machine_code)
self.lbl_status.configure(text="✅ 机器码已复制到剪贴板", text_color="#4CAF50")
self.after(2000, lambda: self.lbl_status.configure(text="等待验证...", text_color="gray"))
callback_id = self.after(2000, self._safe_reset_status)
self.pending_callbacks.append(callback_id)
def _safe_reset_status(self):
"""安全地重置状态提示"""
if not self.is_destroyed:
self.lbl_status.configure(text="等待验证...", text_color="gray")
def _handle_verify(self):
key = self.entry_key.get().strip()
@ -316,6 +637,14 @@ class AuthWindow(ctk.CTk):
self.lbl_status.configure(text="❌ 卡密不能为空", text_color="#F44336")
return
# 如果正在验证中,忽略重复请求
if self.verifying:
return
# 标记为正在验证
self.verifying = True
self.is_verified = False # 重置验证状态
# 锁定UI
self.btn_verify.configure(state="disabled", text="正在连接服务器...")
self.entry_key.configure(state="disabled")
@ -327,27 +656,139 @@ class AuthWindow(ctk.CTk):
def _verify_thread(self, key):
"""后台验证逻辑"""
success, msg, data = self.auth_core.verify_online(key)
# 回到主线程更新UI
self.after(0, lambda: self._on_verify_result(success, msg, key, data))
# 回到主线程更新UI使用安全的方式
if not self.is_destroyed:
callback_id = self.after(0, lambda: self._on_verify_result(success, msg, key, data))
self.pending_callbacks.append(callback_id)
def _on_verify_result(self, success, msg, key, data):
# 如果窗口已销毁,直接返回
if self.is_destroyed:
return
# 标记验证完成
self.verifying = False
self.btn_verify.configure(state="normal", text="立即验证授权")
self.entry_key.configure(state="normal")
if success:
# 验证成功
self.lbl_status.configure(text=f"{msg}", text_color="#4CAF50")
# 验证成功 - 必须先设置 is_verified再关闭窗口
self.is_verified = True
self.lbl_status.configure(text=f"{msg}", text_color="#4CAF50")
# 保存卡密和Token
ConfigManager.save_last_key(key)
self.auth_core.save_token(data)
# 延迟关闭窗口
self.after(1000, self.destroy)
# 如果是自动验证延迟关闭窗口如果是手动验证给用户1秒查看结果后关闭
delay = 800 if self.auto_verify else 1000
callback_id = self.after(delay, self._safe_close_window)
self.pending_callbacks.append(callback_id)
else:
# 验证失败
self.lbl_status.configure(text=f"{msg}", text_color="#F44336")
# 验证失败,确保 is_verified 为 False
self.is_verified = False
# 清除本地缓存
self.auth_core.clear_token()
# 格式化错误消息如果是连接错误显示API地址
error_msg = msg
if "无法连接" in msg or "连接超时" in msg or "服务器" in msg:
# 在错误消息中已经包含了API地址直接显示
pass
elif len(msg) > 80:
# 如果错误消息太长,截断并添加提示
error_msg = msg[:80] + "..."
# 如果是自动验证失败,允许用户修改卡密后重新验证
if self.auto_verify:
self.lbl_status.configure(text=f"{error_msg}\n请检查网络连接或重新输入卡密", text_color="#F44336")
self.auto_verify = False # 重置标志,允许手动验证
else:
self.lbl_status.configure(text=f"{error_msg}", text_color="#F44336")
def _safe_close_window(self):
"""安全地关闭窗口"""
if not self.is_destroyed:
self.is_destroyed = True
# 取消所有pending的after回调
for callback_id in self.pending_callbacks:
try:
self.after_cancel(callback_id)
except:
pass
self.pending_callbacks.clear()
self.destroy()
def _handle_unbind(self):
"""处理解绑操作"""
key = self.entry_key.get().strip()
if not key:
self.lbl_status.configure(text="❌ 卡密不能为空", text_color="#F44336")
return
# 如果正在验证中,忽略重复请求
if self.verifying:
return
# 标记为正在验证
self.verifying = True
self.is_verified = False # 重置验证状态
# 锁定UI
self.btn_verify.configure(state="disabled", text="正在连接服务器...")
self.btn_unbind.configure(state="disabled")
self.entry_key.configure(state="disabled")
self.lbl_status.configure(text="⏳ 正在解绑中,请稍候...", text_color="#2196F3")
# 开启线程进行解绑
threading.Thread(target=self._unbind_thread, args=(key,), daemon=True).start()
def _unbind_thread(self, key):
"""后台解绑逻辑"""
success, msg = self.auth_core.unbind_license(key)
# 回到主线程更新UI使用安全的方式
if not self.is_destroyed:
callback_id = self.after(0, lambda: self._on_unbind_result(success, msg))
self.pending_callbacks.append(callback_id)
def _on_unbind_result(self, success, msg):
# 如果窗口已销毁,直接返回
if self.is_destroyed:
return
# 标记验证完成
self.verifying = False
self.btn_verify.configure(state="normal", text="立即验证授权")
self.btn_unbind.configure(state="normal")
self.entry_key.configure(state="normal")
if success:
# 解绑成功 - 必须先设置 is_verified 为 False再关闭窗口
self.is_verified = False
self.lbl_status.configure(text=f"{msg}", text_color="#4CAF50")
# 清除本地缓存
self.auth_core.clear_token()
# 延迟关闭窗口
callback_id = self.after(800, self._safe_close_window)
self.pending_callbacks.append(callback_id)
else:
# 解绑失败,确保 is_verified 为 False
self.is_verified = False
# 格式化错误消息如果是连接错误显示API地址
error_msg = msg
if "无法连接" in msg or "连接超时" in msg or "服务器" in msg:
# 在错误消息中已经包含了API地址直接显示
pass
elif len(msg) > 80:
# 如果错误消息太长,截断并添加提示
error_msg = msg[:80] + "..."
self.lbl_status.configure(text=f"{error_msg}", text_color="#F44336")
# ==========================================
@ -377,22 +818,20 @@ class AuthValidator:
def validate(self) -> bool:
"""
执行验证流程 (阻塞式)
1. 优先尝试静默验证本地缓存
2. 失败则弹出现代化UI窗口
每次打开都必须进行在线验证防止后台禁用卡密后用户仍能使用
1. 读取保存的卡密如果有
2. 弹出现代化UI窗口并自动验证
3. 验证失败则要求用户重新输入
Returns:
bool: 是否验证成功
"""
# 1. 尝试静默验证 (本地Token有效)
if self.core.check_local_cache():
return True
# 2. 启动 UI 窗口
# 启动 UI 窗口(会自动读取保存的卡密并验证)
app = AuthWindow(self.core)
# 运行主循环 (这会阻塞代码执行,直到窗口关闭)
app.mainloop()
# 3. 窗口关闭后,检查是否验证成功
# 窗口关闭后,检查是否验证成功
return app.is_verified
# --- END OF FILE ---

View File

@ -127,21 +127,42 @@ function exportLicenses() {
// 显示加载状态
submitBtn.disabled = true;
submitText.textContent = '导出中...';
showLoading();
// 使用apiRequest函数处理API请求确保域名配置正确
const apiUrl = '/api/v1/licenses/export';
// 构建API URL复用base.html中的URL构建逻辑
let apiUrl = '/api/v1/licenses/export';
const frontendDomain = window.FRONTEND_DOMAIN || '';
apiRequest(apiUrl, {
if (apiUrl.startsWith('/')) {
if (frontendDomain && !apiUrl.startsWith(frontendDomain)) {
let cleanDomain = frontendDomain;
try {
const urlObj = new URL(frontendDomain.startsWith('http') ? frontendDomain : 'http://' + frontendDomain);
cleanDomain = urlObj.origin;
} catch (e) {
if (frontendDomain.includes('/')) {
cleanDomain = frontendDomain.split('/')[0];
}
}
apiUrl = cleanDomain + apiUrl;
} else if (!frontendDomain) {
apiUrl = window.location.origin + apiUrl;
}
}
// 直接使用fetch处理文件下载不使用apiRequest因为apiRequest会尝试解析JSON
fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'same-origin',
body: JSON.stringify(formData)
})
.then(response => {
// 隐藏加载动画
hideLoading();
// 检查响应状态
if (!response.ok) {
// 处理错误响应
if (response.status === 401) {
@ -154,21 +175,36 @@ function exportLicenses() {
return response.json().then(errorData => {
showNotification(errorData.message || '权限不足,无法执行此操作', 'error');
throw new Error(`403: ${errorData.message || '权限不足'}`);
}).catch(() => {
showNotification('权限不足,无法执行此操作', 'error');
throw new Error('403: 权限不足');
});
} else {
// 尝试解析错误信息
return response.json().then(errorData => {
showNotification(errorData.message || `导出失败: ${response.statusText}`, 'error');
throw new Error(`${response.status}: ${errorData.message || response.statusText}`);
}).catch(() => {
showNotification(`导出失败: ${response.statusText}`, 'error');
throw new Error(`${response.status}: ${response.statusText}`);
});
}
}
// 成功响应,处理文件下载
// 获取文件名
const contentDisposition = response.headers.get('Content-Disposition');
let filename = 'licenses.xlsx';
if (contentDisposition) {
const filenameMatch = contentDisposition.match(/filename="?([^"]+)"?/);
if (filenameMatch && filenameMatch.length === 2) {
filename = filenameMatch[1];
const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (filenameMatch && filenameMatch[1]) {
filename = filenameMatch[1].replace(/['"]/g, '');
// 处理URL编码的文件名
try {
filename = decodeURIComponent(filename);
} catch (e) {
// 如果解码失败,使用原始文件名
}
}
}
@ -187,10 +223,14 @@ function exportLicenses() {
});
})
.catch(error => {
// 隐藏加载动画
hideLoading();
console.error('Failed to export licenses:', error);
showNotification(error.message || '导出失败', 'error');
// 如果错误消息不是我们自定义的,显示通用错误消息
if (error.message && !error.message.includes(':')) {
showNotification('导出失败: ' + error.message, 'error');
} else if (!error.message.includes('未授权') && !error.message.includes('权限不足')) {
showNotification('导出失败,请稍后重试', 'error');
}
})
.finally(() => {
// 恢复按钮状态

View File

@ -1,512 +1,30 @@
2025-11-22 13:55:38,804 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 13:55:38,805 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 13:55:38,805 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 13:55:39,522 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 13:55:39,522 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 13:55:39,522 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 13:55:39,716 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 13:55:39,716 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 13:55:39,716 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 13:55:39,716 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 13:55:39,717 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 13:55:39,717 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 13:55:39,717 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 13:55:39,717 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 13:55:39,717 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 13:55:39,741 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 13:55:39,741 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 13:55:39,741 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 13:55:39,741 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 13:55:39,741 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 13:55:39,741 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:01:08,267 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:01:08,269 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:01:08,269 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:01:08,772 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:01:08,772 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:01:08,772 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:16:26,892 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:16:28,910 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:16:35,620 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:16:38,916 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:17:58,265 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:18:34,569 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:18:34,569 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:18:34,569 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:18:34,672 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:18:34,672 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:18:34,672 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:18:34,678 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:18:34,725 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:18:34,725 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:18:34,725 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:18:34,725 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:18:34,725 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:18:34,725 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:19:05,591 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:09,526 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:09,526 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:09,526 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:09,713 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:09,713 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:09,713 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:09,718 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:09,768 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:09,768 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:09,768 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:09,768 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:09,768 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:09,768 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:28,147 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:44,899 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:44,900 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:44,900 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:45,004 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:45,004 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:45,004 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:45,816 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:45,816 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:45,816 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:45,816 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:20:45,817 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:45,817 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:45,817 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:45,817 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:45,817 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:124]
2025-11-22 14:20:45,863 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:45,863 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:45,863 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:45,863 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:45,863 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:97]
2025-11-22 14:20:48,483 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:22:06,425 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:14,494 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:17,463 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:21,236 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:27,394 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:42,403 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:45,366 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:48,416 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:54,112 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:33:57,389 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:34:00,308 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:34:05,899 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:34:09,907 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:34:12,749 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:34:17,656 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:34:21,503 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:35:11,341 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:36:11,672 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:39:01,872 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:49:36,143 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:51:13,517 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:52:10,815 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:90]
2025-11-22 14:52:41,540 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 14:53:40,921 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 14:53:53,684 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 14:54:34,239 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 14:55:10,790 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 14:57:43,127 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 14:58:08,288 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:02:04,360 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:04:04,791 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:05:01,631 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:14:18,452 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:14:27,680 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:14:32,172 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:32:08,417 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:32:10,523 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:37:03,632 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:37:04,201 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:39:03,782 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:51:13,296 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:52:56,460 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 15:53:03,081 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:05:58,969 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:19:00,353 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:19:02,601 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:19:05,618 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:19:37,150 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:19:39,315 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:19:49,441 ERROR: 获取卡密列表失败: (pymysql.err.OperationalError) (1054, "Unknown column 'device.ip_address' in 'field list'")
[SQL: SELECT device.device_id AS device_device_id, device.machine_code AS device_machine_code, device.license_id AS device_license_id, device.product_id AS device_product_id, device.software_version AS device_software_version, device.ip_address AS device_ip_address, device.status AS device_status, device.activate_time AS device_activate_time, device.last_verify_time AS device_last_verify_time, device.create_time AS device_create_time, device.update_time AS device_update_time
FROM device
WHERE %(param_1)s = device.license_id
LIMIT %(param_2)s]
[parameters: {'param_1': 2, 'param_2': 1}]
(Background on this error at: https://sqlalche.me/e/20/e3q8) [in D:\work\code\python\KaMiXiTong\master\app\api\license.py:86]
2025-11-22 17:19:49,446 ERROR: 获取工单列表失败: (pymysql.err.OperationalError) (1054, "Unknown column 'product.image_path' in 'field list'")
[SQL: SELECT product.product_id AS product_product_id, product.product_name AS product_product_name, product.description AS product_description, product.image_path AS product_image_path, product.status AS product_status, product.create_time AS product_create_time, product.update_time AS product_update_time
FROM product
WHERE product.product_id = %(pk_1)s]
[parameters: {'pk_1': 'KMX002'}]
(Background on this error at: https://sqlalche.me/e/20/e3q8) [in D:\work\code\python\KaMiXiTong\master\app\api\ticket.py:51]
2025-11-22 17:19:51,613 ERROR: 获取产品列表失败: (pymysql.err.OperationalError) (1054, "Unknown column 'product.image_path' in 'field list'")
[SQL: SELECT product.product_id AS product_product_id, product.product_name AS product_product_name, product.description AS product_description, product.image_path AS product_image_path, product.status AS product_status, product.create_time AS product_create_time, product.update_time AS product_update_time
FROM product ORDER BY product.create_time DESC
LIMIT %(param_1)s, %(param_2)s]
[parameters: {'param_1': 0, 'param_2': 10}]
(Background on this error at: https://sqlalche.me/e/20/e3q8) [in D:\work\code\python\KaMiXiTong\master\app\api\product.py:114]
2025-11-22 17:19:51,614 ERROR: 错误类型: <class 'sqlalchemy.exc.OperationalError'> [in D:\work\code\python\KaMiXiTong\master\app\api\product.py:115]
2025-11-22 17:19:51,640 ERROR: 错误堆栈: Traceback (most recent call last):
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context
self.dialect.do_execute(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\default.py", line 951, in do_execute
cursor.execute(statement, parameters)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\cursors.py", line 153, in execute
result = self._query(query)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\cursors.py", line 322, in _query
conn.query(q)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\connections.py", line 558, in query
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\connections.py", line 822, in _read_query_result
result.read()
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\connections.py", line 1200, in read
first_packet = self.connection._read_packet()
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\connections.py", line 772, in _read_packet
packet.raise_for_error()
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\protocol.py", line 221, in raise_for_error
err.raise_mysql_exception(self._data)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\err.py", line 143, in raise_mysql_exception
raise errorclass(errno, errval)
pymysql.err.OperationalError: (1054, "Unknown column 'product.image_path' in 'field list'")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "D:\work\code\python\KaMiXiTong\master\app\api\product.py", line 35, in get_products
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
File "D:\IDE\Language\Python3.10\lib\site-packages\flask_sqlalchemy\query.py", line 98, in paginate
return QueryPagination(
File "D:\IDE\Language\Python3.10\lib\site-packages\flask_sqlalchemy\pagination.py", line 72, in __init__
items = self._query_items()
File "D:\IDE\Language\Python3.10\lib\site-packages\flask_sqlalchemy\pagination.py", line 358, in _query_items
out = query.limit(self.per_page).offset(self._query_offset).all()
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\orm\query.py", line 2704, in all
return self._iter().all() # type: ignore
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\orm\query.py", line 2857, in _iter
result: Union[ScalarResult[_T], Result[_T]] = self.session.execute(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\orm\session.py", line 2365, in execute
return self._execute_internal(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\orm\session.py", line 2251, in _execute_internal
result: Result[Any] = compile_state_cls.orm_execute_statement(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\orm\context.py", line 306, in orm_execute_statement
result = conn.execute(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\base.py", line 1419, in execute
return meth(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\sql\elements.py", line 526, in _execute_on_connection
return connection._execute_clauseelement(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\base.py", line 1641, in _execute_clauseelement
ret = self._execute_context(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\base.py", line 1846, in _execute_context
return self._exec_single_context(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\base.py", line 1986, in _exec_single_context
self._handle_dbapi_exception(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\base.py", line 2355, in _handle_dbapi_exception
raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\base.py", line 1967, in _exec_single_context
self.dialect.do_execute(
File "D:\IDE\Language\Python3.10\lib\site-packages\sqlalchemy\engine\default.py", line 951, in do_execute
cursor.execute(statement, parameters)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\cursors.py", line 153, in execute
result = self._query(query)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\cursors.py", line 322, in _query
conn.query(q)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\connections.py", line 558, in query
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\connections.py", line 822, in _read_query_result
result.read()
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\connections.py", line 1200, in read
first_packet = self.connection._read_packet()
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\connections.py", line 772, in _read_packet
packet.raise_for_error()
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\protocol.py", line 221, in raise_for_error
err.raise_mysql_exception(self._data)
File "D:\IDE\Language\Python3.10\lib\site-packages\pymysql\err.py", line 143, in raise_mysql_exception
raise errorclass(errno, errval)
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1054, "Unknown column 'product.image_path' in 'field list'")
[SQL: SELECT product.product_id AS product_product_id, product.product_name AS product_product_name, product.description AS product_description, product.image_path AS product_image_path, product.status AS product_status, product.create_time AS product_create_time, product.update_time AS product_update_time
FROM product ORDER BY product.create_time DESC
LIMIT %(param_1)s, %(param_2)s]
[parameters: {'param_1': 0, 'param_2': 10}]
(Background on this error at: https://sqlalche.me/e/20/e3q8)
[in D:\work\code\python\KaMiXiTong\master\app\api\product.py:116]
2025-11-22 17:19:53,334 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:19:54,293 ERROR: 获取版本列表失败: (pymysql.err.OperationalError) (1054, "Unknown column 'product.image_path' in 'field list'")
[SQL: SELECT product.product_id AS product_product_id, product.product_name AS product_product_name, product.description AS product_description, product.image_path AS product_image_path, product.status AS product_status, product.create_time AS product_create_time, product.update_time AS product_update_time
FROM product
WHERE product.product_id = %(pk_1)s]
[parameters: {'pk_1': 'KMX001'}]
(Background on this error at: https://sqlalche.me/e/20/e3q8) [in D:\work\code\python\KaMiXiTong\master\app\api\version.py:95]
2025-11-22 17:49:42,406 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:93]
2025-11-22 17:54:30,685 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 17:54:30,685 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 17:54:30,685 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 17:54:30,761 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:102]
2025-11-22 17:54:30,761 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:102]
2025-11-22 18:07:30,522 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:07:52,962 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:08:18,771 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:08:52,526 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:09:23,276 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:11:24,010 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:11:26,101 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:12:16,035 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:14:23,758 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:15:27,120 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:17:18,450 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:17:36,021 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:17:55,799 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:20:25,143 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:20:30,476 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:20:42,085 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:25:16,676 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:25:50,791 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:25:59,449 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:25:59,450 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 18:25:59,450 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 18:25:59,530 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 18:25:59,530 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 18:26:01,777 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:33:53,761 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:34:38,040 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:34:44,403 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:34:55,336 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:38:45,350 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:38:58,823 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:39:14,999 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:39:22,581 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:39:26,766 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:39:36,244 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:39:50,033 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:40:08,697 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:41:26,671 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:41:57,721 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:42:02,929 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:42:09,889 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:42:37,991 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:49:36,795 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:50:27,639 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:53:10,619 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 18:56:28,784 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:01:21,164 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:05:32,038 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:08:32,055 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:09:24,505 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:09:26,624 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:13:29,947 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:13:31,966 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:13:43,157 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:13:43,157 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:13:43,157 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:13:43,231 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:13:43,231 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:13:45,063 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:13:45,608 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:13:45,626 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:14:32,074 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:14:32,107 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:14:48,947 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:14:50,939 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:14:56,703 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:14:56,703 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:14:56,703 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:14:56,781 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:14:56,781 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:15:22,725 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:15:24,710 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:15:30,503 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:15:30,504 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:15:30,504 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:15:30,590 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:15:30,590 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:16:51,599 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:16:53,602 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:16:59,280 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:16:59,280 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:16:59,280 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:16:59,358 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:16:59,358 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:17:28,106 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:17:30,084 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:17:30,106 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:17:57,729 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:17:57,741 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:18:02,013 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:18:02,041 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:18:06,657 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:18:38,212 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:19:14,854 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:19:37,065 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:19:37,093 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:19:41,235 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:20:09,482 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:20:09,504 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:20:28,322 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:20:28,528 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:20:55,112 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:20:55,550 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:20:56,165 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:20:59,715 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:21:01,348 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:21:06,074 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:21:28,533 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:21:28,588 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:21:33,756 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:21:33,801 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:21:36,916 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:22:13,034 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:22:13,198 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:22:22,864 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:22:23,782 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:22:57,380 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:23:39,526 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:23:40,026 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:23:43,677 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:25:01,614 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:25:01,636 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:25:03,898 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:25:20,641 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:25:20,646 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:26:03,235 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:26:03,267 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:27:27,259 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:27:29,281 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:28:42,916 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:28:44,890 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:29:00,912 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:29:00,913 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:29:00,913 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:29:01,013 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:29:01,013 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:29:13,360 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:29:36,518 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:30:01,137 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:30:07,077 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:30:37,634 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:30:37,635 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:30:37,635 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:30:37,799 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:30:37,799 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:30:47,683 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:30:47,683 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:30:47,683 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:135]
2025-11-22 19:30:47,771 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:30:47,771 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\app\__init__.py:112]
2025-11-22 19:30:49,172 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:30:50,516 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:30:50,616 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:31:24,007 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:31:41,206 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:31:43,668 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:31:43,684 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:32:03,384 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:32:09,346 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:32:12,820 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:32:12,844 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:32:18,646 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:32:20,713 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:32:20,778 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:34:03,452 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:34:03,808 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:34:58,912 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:34:58,920 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:35:09,603 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:35:10,698 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:35:11,877 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:35:22,244 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:36:09,190 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:36:11,209 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:36:21,061 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:37:19,952 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:37:22,358 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:38:08,328 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:38:08,969 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:38:27,331 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:38:28,654 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:38:53,412 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:38:54,347 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:39:34,896 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:39:36,570 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:39:42,704 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:40:04,082 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:40:04,242 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:40:35,434 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:40:36,068 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:40:47,459 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:40:48,891 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:41:08,666 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:41:11,486 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:41:34,821 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:41:36,574 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:41:42,050 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:42:04,086 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:42:05,550 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:42:29,413 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:42:31,687 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:46:28,599 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:101]
2025-11-22 19:46:39,323 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:46:51,491 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:47:05,130 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:48:24,813 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:48:31,647 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:50:37,904 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:52:10,038 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:52:32,716 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:53:00,764 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:53:20,558 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:53:32,123 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:53:47,730 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:54:07,270 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:56:31,516 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:56:59,758 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:57:40,236 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:57:42,331 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:57:46,314 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:58:00,490 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 19:59:41,869 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:00:12,514 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:00:44,156 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:01:08,184 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:02:08,830 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:02:32,602 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:02:36,742 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:03:14,238 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:05:40,769 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:06:21,991 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:06:32,752 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:06:53,895 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:07:05,128 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:07:14,246 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:08:41,509 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:10:26,059 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:10:50,519 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:12:32,425 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:12:37,515 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:12:41,438 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:14:00,258 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:14:11,317 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:14:23,392 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:14:41,554 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:14:48,223 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:14:52,651 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:14:58,750 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:18:15,310 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:18:25,997 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-22 20:18:42,041 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:105]
2025-11-25 22:17:52,630 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:54,791 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:54,791 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:54,970 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:54,970 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:54,970 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,118 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,118 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,118 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,118 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,267 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,267 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,267 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,267 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,267 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,416 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,416 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,416 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,416 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,416 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,416 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,587 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,587 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,587 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,587 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,587 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,587 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:17:55,587 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]
2025-11-25 22:20:26,716 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\.\config.py:110]
2025-11-25 22:20:40,356 INFO: KaMiXiTong startup [in D:\work\code\python\KaMiXiTong\master\config.py:110]

View File

@ -1,99 +1,10 @@
#!/usr/bin/env python3
"""测试 Werkzeug base64 编码方式"""
from app.utils.auth_validator import AuthValidator
from werkzeug.security import generate_password_hash, check_password_hash
import base64
import hashlib
# 测试旧格式的密码哈希(数据库中的格式)
old_hash = "$pbkdf2-sha256$29000$N2aBd1I5Eaz5bYY2CXbu2A$1lEXwDoX9S5slrv0cFHsQ8fAj55m43.1mPbX5f.Ra0U"
password = "admin123"
print(f"旧格式哈希: {old_hash}")
print(f"密码: {password}")
# 尝试使用 Werkzeug 验证
try:
result = check_password_hash(old_hash, password)
print(f"Werkzeug 验证结果: {result}")
except Exception as e:
print(f"Werkzeug 验证失败: {e}")
# 手动解析旧格式
if old_hash.startswith('$pbkdf2-sha256$'):
parts = old_hash.split('$')
print(f"\n部分数量: {len(parts)}")
if len(parts) == 5:
iterations = int(parts[2])
salt_str = parts[3]
hash_str = parts[4]
print(f"\n迭代次数: {iterations}")
print(f"Salt 字符串: {salt_str}")
print(f"Salt 长度: {len(salt_str)}")
print(f"Hash 字符串: {hash_str}")
print(f"Hash 长度: {len(hash_str)}")
# 尝试 base64 解码
try:
# 添加填充
def add_padding(s):
missing = len(s) % 4
return s + '=' * (4 - missing) if missing else s
# 处理点号 - 可能是 URL-safe base64 的变体
# 点号在 base64 中不存在,可能是其他字符的编码
# 尝试将点号替换为可能的 base64 字符
def fix_base64(s):
# 尝试不同的替换方式
# 点号可能是 + 或 / 的编码错误
s1 = s.replace('.', '+')
s2 = s.replace('.', '/')
s3 = s.replace('.', '=')
return [s, s1, s2, s3]
salt_padded = add_padding(salt_str)
hash_variants = [add_padding(h) for h in fix_base64(hash_str)]
print(f"\nSalt 填充后: {salt_padded}")
print(f"Hash 变体数量: {len(hash_variants)}")
salt_bytes = base64.b64decode(salt_padded, validate=False)
# 尝试所有变体
for i, hash_padded in enumerate(hash_variants):
try:
print(f"\n尝试 Hash 变体 {i}: {hash_padded[:50]}...")
hash_bytes = base64.b64decode(hash_padded, validate=False)
print(f"变体 {i} 解码成功,长度: {len(hash_bytes)}")
# 计算 PBKDF2
password_bytes = password.encode('utf-8')
computed = hashlib.pbkdf2_hmac('sha256', password_bytes, salt_bytes, iterations)
import hmac
match = hmac.compare_digest(computed, hash_bytes)
print(f"变体 {i} 哈希匹配: {match}")
if match:
break
except Exception as e:
print(f"变体 {i} 失败: {e}")
print(f"\nSalt 解码成功,长度: {len(salt_bytes)}")
print(f"Hash 解码成功,长度: {len(hash_bytes)}")
# 计算 PBKDF2
password_bytes = password.encode('utf-8')
computed = hashlib.pbkdf2_hmac('sha256', password_bytes, salt_bytes, iterations)
print(f"\n计算的哈希长度: {len(computed)}")
print(f"存储的哈希长度: {len(hash_bytes)}")
import hmac
match = hmac.compare_digest(computed, hash_bytes)
print(f"\n哈希匹配: {match}")
except Exception as e:
print(f"\n解码/验证失败: {e}")
import traceback
traceback.print_exc()
validator = AuthValidator(software_id="ArticleReplace",
api_url="http://km.taisan.online/api/v1",
gui_mode=True
)
# 执行验证
if not validator.validate():
print("授权验证失败,程序退出")