Exeprotector/database.py
2025-08-08 18:28:46 +08:00

255 lines
9.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import mysql.connector
from mysql.connector import Error
import uuid
from datetime import datetime, timedelta
import hashlib
import os
class LicenseDatabase:
def __init__(self, host=None, database=None, user=None, password=None):
self.host = host or os.environ.get('DB_HOST', 'taiyiagi.xyz')
self.database = database or os.environ.get('DB_NAME', 'filesend')
self.user = user or os.environ.get('DB_USER', 'taiyi')
self.password = password or os.environ.get('DB_PASSWORD', 'taiyi1224')
self.connection = None
def connect(self):
"""连接到数据库"""
try:
self.connection = mysql.connector.connect(
host=self.host,
database=self.database,
user=self.user,
password=self.password
)
if self.connection.is_connected():
return True
return False
except Error as e:
print(f"数据库连接错误: {e}")
return False
def create_tables(self):
"""创建必要的数据库表"""
if not self.connection or not self.connection.is_connected():
if not self.connect():
return False
try:
cursor = self.connection.cursor()
# 创建卡密表
cursor.execute('''
CREATE TABLE IF NOT EXISTS license_keys (
id INT AUTO_INCREMENT PRIMARY KEY,
key_code VARCHAR(50) NOT NULL UNIQUE,
machine_code VARCHAR(100) DEFAULT NULL,
start_time DATETIME DEFAULT NULL,
end_time DATETIME NOT NULL,
status ENUM('unused', 'active', 'expired', 'banned') DEFAULT 'unused',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
''')
self.connection.commit()
cursor.close()
return True
except Error as e:
print(f"创建表错误: {e}")
return False
def generate_key(self, days_valid):
"""生成一个新的卡密并保存到数据库"""
if not self.connection or not self.connection.is_connected():
if not self.connect():
return None
try:
# 生成UUID作为基础然后进行哈希处理
key_uuid = uuid.uuid4().hex
hash_obj = hashlib.sha256(key_uuid.encode())
# 取前20个字符作为卡密
key_code = hash_obj.hexdigest()[:20].upper()
# 格式化卡密每5个字符一组
formatted_key = '-'.join([key_code[i:i + 5] for i in range(0, len(key_code), 5)])
# 计算过期时间
end_time = datetime.now() + timedelta(days=days_valid)
cursor = self.connection.cursor()
query = """
INSERT INTO license_keys (key_code, end_time)
VALUES (%s, %s)
"""
cursor.execute(query, (formatted_key, end_time))
self.connection.commit()
cursor.close()
return formatted_key
except Error as e:
print(f"生成卡密错误: {e}")
return None
def validate_key(self, key_code, machine_code):
"""验证卡密是否有效,并绑定机器码 - 严格一机一码"""
if not self.connection or not self.connection.is_connected():
if not self.connect():
return False, "数据库连接失败"
try:
cursor = self.connection.cursor(dictionary=True)
# 查询卡密信息
query = "SELECT * FROM license_keys WHERE key_code = %s"
cursor.execute(query, (key_code,))
key_info = cursor.fetchone()
if not key_info:
cursor.close()
return False, "无效的激活码"
# 检查卡密状态
if key_info['status'] == 'banned':
cursor.close()
return False, "激活码已被封禁"
if key_info['status'] == 'expired':
cursor.close()
return False, "激活码已过期"
# 一机一码严格检查:每个激活码只能在一台机器上使用
if key_info['status'] == 'active':
if key_info['machine_code'] != machine_code:
# 这个码已经用过了,不能再次使用
cursor.close()
return False, f"此激活码已在设备{key_info['machine_code'][:8]}...上使用,一个激活码只能在一台设备上使用一次"
else:
# 已经激活过这台机器,验证是否过期
if datetime.now() > key_info['end_time']:
update_query = "UPDATE license_keys SET status = 'expired' WHERE key_code = %s"
cursor.execute(update_query, (key_code,))
self.connection.commit()
cursor.close()
return False, "激活码已过期"
else:
cursor.close()
return True, "此设备已激活,继续使用"
# 首次激活:验证通过后绑定到机器
if key_info['status'] == 'unused':
update_query = """
UPDATE license_keys
SET status = 'active', machine_code = %s, start_time = %s
WHERE key_code = %s AND status = 'unused'
"""
cursor.execute(update_query, (machine_code, datetime.now(), key_code))
rows_affected = cursor.rowcount
self.connection.commit()
if rows_affected == 0:
# 可能已经被其他并发操作激活
cursor.close()
return False, "激活码已被使用,请使用新的激活码"
# 再次检查是否过期(防止并发问题)
final_check_query = "SELECT * FROM license_keys WHERE key_code = %s"
cursor.execute(final_check_query, (key_code,))
final_info = cursor.fetchone()
if final_info and datetime.now() > final_info['end_time']:
update_query = "UPDATE license_keys SET status = 'expired' WHERE key_code = %s"
cursor.execute(update_query, (key_code,))
self.connection.commit()
cursor.close()
return False, "激活码已过期"
cursor.close()
return True, "激活成功"
except Error as e:
print(f"验证激活码错误: {e}")
return False, f"验证过程出错: {str(e)}"
def get_all_keys(self):
"""获取所有卡密信息"""
if not self.connection or not self.connection.is_connected():
if not self.connect():
return []
try:
cursor = self.connection.cursor(dictionary=True)
query = "SELECT * FROM license_keys ORDER BY created_at DESC"
cursor.execute(query)
keys = cursor.fetchall()
cursor.close()
return keys
except Error as e:
print(f"获取卡密列表错误: {e}")
return []
def update_key_status(self, key_code, status):
"""更新卡密状态"""
if not self.connection or not self.connection.is_connected():
if not self.connect():
return False
try:
cursor = self.connection.cursor()
query = "UPDATE license_keys SET status = %s WHERE key_code = %s"
cursor.execute(query, (status, key_code))
self.connection.commit()
cursor.close()
return True
except Error as e:
print(f"更新卡密状态错误: {e}")
return False
def release_key(self, key_code):
"""释放已使用的激活码 - 将其重置为未使用状态,清空机器码"""
if not self.connection or not self.connection.is_connected():
if not self.connect():
return False, "数据库连接失败"
try:
cursor = self.connection.cursor(dictionary=True)
# 检查卡密是否存在且处于已激活状态
check_query = "SELECT * FROM license_keys WHERE key_code = %s"
cursor.execute(check_query, (key_code,))
key_info = cursor.fetchone()
if not key_info:
cursor.close()
return False, "激活码不存在"
if key_info['status'] != 'active':
cursor.close()
return False, f"激活码处于 {key_info['status']} 状态,只能释放已使用的激活码"
# 释放激活码:重置为未使用状态,清空机器码和开始时间
release_query = """
UPDATE license_keys
SET status = 'unused', machine_code = NULL, start_time = NULL
WHERE key_code = %s
"""
cursor.execute(release_query, (key_code,))
rows_affected = cursor.rowcount
self.connection.commit()
cursor.close()
if rows_affected > 0:
return True, "激活码已释放,可以重新使用"
else:
return False, "释放激活码失败"
except Error as e:
print(f"释放激活码错误: {e}")
return False, f"释放过程出错: {str(e)}"
def close(self):
"""关闭数据库连接"""
if self.connection and self.connection.is_connected():
self.connection.close()