2025-04-02 10:15:41 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
"""
|
|
|
|
|
|
Cursor自动化服务 - 交互式环境配置脚本
|
|
|
|
|
|
用于配置和初始化新服务器环境、数据库和Redis
|
|
|
|
|
|
"""
|
|
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import subprocess
|
|
|
|
|
|
import getpass
|
|
|
|
|
|
import time
|
|
|
|
|
|
import yaml
|
|
|
|
|
|
import socket
|
|
|
|
|
|
import random
|
|
|
|
|
|
import string
|
|
|
|
|
|
from typing import Dict, Any, Optional, Tuple
|
|
|
|
|
|
|
|
|
|
|
|
# 设置颜色输出
|
|
|
|
|
|
class Colors:
|
|
|
|
|
|
HEADER = '\033[95m'
|
|
|
|
|
|
BLUE = '\033[94m'
|
|
|
|
|
|
GREEN = '\033[92m'
|
|
|
|
|
|
YELLOW = '\033[93m'
|
|
|
|
|
|
RED = '\033[91m'
|
|
|
|
|
|
ENDC = '\033[0m'
|
|
|
|
|
|
BOLD = '\033[1m'
|
|
|
|
|
|
UNDERLINE = '\033[4m'
|
|
|
|
|
|
|
|
|
|
|
|
def print_color(text, color):
|
|
|
|
|
|
"""输出彩色文本"""
|
|
|
|
|
|
print(f"{color}{text}{Colors.ENDC}")
|
|
|
|
|
|
|
|
|
|
|
|
def print_title(title):
|
|
|
|
|
|
"""打印标题"""
|
|
|
|
|
|
width = 60
|
|
|
|
|
|
print("\n" + "=" * width)
|
|
|
|
|
|
print(f"{Colors.BOLD}{Colors.HEADER}{title.center(width)}{Colors.ENDC}")
|
|
|
|
|
|
print("=" * width + "\n")
|
|
|
|
|
|
|
|
|
|
|
|
def print_step(step, description):
|
|
|
|
|
|
"""打印步骤信息"""
|
|
|
|
|
|
print(f"{Colors.BOLD}{Colors.BLUE}[步骤 {step}]{Colors.ENDC} {description}")
|
|
|
|
|
|
|
|
|
|
|
|
def print_success(message):
|
|
|
|
|
|
"""打印成功信息"""
|
|
|
|
|
|
print(f"{Colors.GREEN}✓ {message}{Colors.ENDC}")
|
|
|
|
|
|
|
|
|
|
|
|
def print_warning(message):
|
|
|
|
|
|
"""打印警告信息"""
|
|
|
|
|
|
print(f"{Colors.YELLOW}⚠ {message}{Colors.ENDC}")
|
|
|
|
|
|
|
|
|
|
|
|
def print_error(message):
|
|
|
|
|
|
"""打印错误信息"""
|
|
|
|
|
|
print(f"{Colors.RED}✗ {message}{Colors.ENDC}")
|
|
|
|
|
|
|
2025-04-02 10:45:19 +08:00
|
|
|
|
# 检测虚拟环境
|
|
|
|
|
|
def is_in_virtualenv():
|
|
|
|
|
|
"""检查是否在虚拟环境中运行"""
|
|
|
|
|
|
return hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)
|
|
|
|
|
|
|
|
|
|
|
|
def setup_virtualenv():
|
|
|
|
|
|
"""设置虚拟环境"""
|
|
|
|
|
|
print_title("虚拟环境检测")
|
|
|
|
|
|
|
|
|
|
|
|
if is_in_virtualenv():
|
|
|
|
|
|
print_success("已在虚拟环境中运行")
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
print_warning("当前不在虚拟环境中运行")
|
|
|
|
|
|
create_venv = input("是否创建并使用虚拟环境? (推荐) (y/n, 默认: y): ").strip().lower()
|
|
|
|
|
|
|
|
|
|
|
|
if create_venv == 'n':
|
|
|
|
|
|
print_warning("跳过虚拟环境创建,将直接在系统Python环境中安装依赖")
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
venv_path = input("请输入虚拟环境路径 (默认: ./venv): ").strip() or "./venv"
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 检查venv模块
|
|
|
|
|
|
try:
|
|
|
|
|
|
import venv
|
|
|
|
|
|
has_venv = True
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
has_venv = False
|
|
|
|
|
|
|
|
|
|
|
|
if not has_venv:
|
|
|
|
|
|
print_warning("Python venv模块不可用,尝试安装...")
|
|
|
|
|
|
if sys.platform.startswith('linux'):
|
|
|
|
|
|
print("在Linux上安装venv模块...")
|
|
|
|
|
|
try:
|
|
|
|
|
|
subprocess.check_call(["sudo", "apt", "install", "-y", "python3-venv", "python3-full"])
|
|
|
|
|
|
except:
|
|
|
|
|
|
try:
|
|
|
|
|
|
subprocess.check_call(["sudo", "yum", "install", "-y", "python3-venv"])
|
|
|
|
|
|
except:
|
|
|
|
|
|
print_error("无法自动安装venv模块,请手动安装后重试")
|
|
|
|
|
|
print_warning("Ubuntu/Debian: sudo apt install python3-venv python3-full")
|
|
|
|
|
|
print_warning("CentOS/RHEL: sudo yum install python3-venv")
|
|
|
|
|
|
return False
|
|
|
|
|
|
else:
|
|
|
|
|
|
print_error("请安装Python venv模块后重试")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# 创建虚拟环境
|
|
|
|
|
|
print_step("创建", f"正在创建虚拟环境: {venv_path}")
|
|
|
|
|
|
if sys.platform.startswith('win'):
|
|
|
|
|
|
subprocess.check_call([sys.executable, "-m", "venv", venv_path])
|
|
|
|
|
|
else:
|
|
|
|
|
|
subprocess.check_call([sys.executable, "-m", "venv", venv_path])
|
|
|
|
|
|
|
|
|
|
|
|
# 计算激活脚本路径
|
|
|
|
|
|
if sys.platform.startswith('win'):
|
|
|
|
|
|
activate_script = os.path.join(venv_path, "Scripts", "activate.bat")
|
|
|
|
|
|
python_path = os.path.join(venv_path, "Scripts", "python.exe")
|
|
|
|
|
|
else:
|
|
|
|
|
|
activate_script = os.path.join(venv_path, "bin", "activate")
|
|
|
|
|
|
python_path = os.path.join(venv_path, "bin", "python")
|
|
|
|
|
|
|
|
|
|
|
|
print_success(f"虚拟环境创建成功: {venv_path}")
|
|
|
|
|
|
print_warning("请退出当前程序,然后执行以下命令激活虚拟环境并重新运行:")
|
|
|
|
|
|
|
|
|
|
|
|
if sys.platform.startswith('win'):
|
|
|
|
|
|
print(f"\n{Colors.BOLD}Windows:{Colors.ENDC}")
|
|
|
|
|
|
print(f" {activate_script}")
|
|
|
|
|
|
print(f" python setup_environment.py")
|
|
|
|
|
|
else:
|
|
|
|
|
|
print(f"\n{Colors.BOLD}Linux/Mac:{Colors.ENDC}")
|
|
|
|
|
|
print(f" source {activate_script}")
|
|
|
|
|
|
print(f" python setup_environment.py")
|
|
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print_error(f"创建虚拟环境失败: {str(e)}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# 尝试导入必要的库,如果不存在则安装
|
|
|
|
|
|
required_packages = [
|
|
|
|
|
|
"loguru",
|
|
|
|
|
|
"pymysql",
|
|
|
|
|
|
"aiomysql",
|
|
|
|
|
|
"redis",
|
|
|
|
|
|
"pyyaml",
|
|
|
|
|
|
"cryptography" # 添加这一行
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2025-04-02 10:15:41 +08:00
|
|
|
|
def check_and_install_packages():
|
|
|
|
|
|
"""检查并安装必要的Python包"""
|
|
|
|
|
|
print_step(1, "检查并安装必要的Python包")
|
|
|
|
|
|
|
|
|
|
|
|
for package in required_packages:
|
|
|
|
|
|
try:
|
|
|
|
|
|
__import__(package.split(">=")[0])
|
|
|
|
|
|
print_success(f"已安装: {package}")
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
print_warning(f"未找到 {package},正在安装...")
|
|
|
|
|
|
try:
|
|
|
|
|
|
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
|
|
|
|
|
|
print_success(f"成功安装 {package}")
|
|
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
|
|
print_error(f"安装 {package} 失败: {str(e)}")
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
# 安装后重新导入必要的库
|
|
|
|
|
|
try:
|
|
|
|
|
|
global yaml
|
|
|
|
|
|
import yaml
|
|
|
|
|
|
from loguru import logger
|
|
|
|
|
|
print_success("所有必要的包已安装")
|
|
|
|
|
|
except ImportError as e:
|
|
|
|
|
|
print_error(f"无法导入必要的库: {str(e)}")
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
def generate_password(length=16):
|
|
|
|
|
|
"""生成随机密码"""
|
|
|
|
|
|
chars = string.ascii_letters + string.digits + "!@#$%^&*()_+-="
|
|
|
|
|
|
return ''.join(random.choice(chars) for _ in range(length))
|
|
|
|
|
|
|
|
|
|
|
|
def get_default_hostname():
|
|
|
|
|
|
"""获取默认主机名"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
return socket.gethostname()
|
|
|
|
|
|
except:
|
|
|
|
|
|
return "cursor-server"
|
|
|
|
|
|
|
|
|
|
|
|
def read_config():
|
|
|
|
|
|
"""读取现有配置文件"""
|
|
|
|
|
|
config_path = "config.yaml"
|
|
|
|
|
|
if os.path.exists(config_path):
|
|
|
|
|
|
try:
|
|
|
|
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
|
|
|
|
return yaml.safe_load(f)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print_warning(f"读取配置文件失败: {str(e)}")
|
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
def configure_server_settings(existing_config):
|
|
|
|
|
|
"""配置服务器设置"""
|
|
|
|
|
|
print_step(2, "配置服务器标识")
|
|
|
|
|
|
|
|
|
|
|
|
existing_hostname = None
|
|
|
|
|
|
if 'server_config' in existing_config and 'hostname' in existing_config['server_config']:
|
|
|
|
|
|
existing_hostname = existing_config['server_config']['hostname']
|
|
|
|
|
|
|
|
|
|
|
|
default_hostname = existing_hostname or get_default_hostname()
|
|
|
|
|
|
hostname = input(f"请输入服务器唯一标识 (默认: {default_hostname}): ").strip()
|
|
|
|
|
|
if not hostname:
|
|
|
|
|
|
hostname = default_hostname
|
|
|
|
|
|
|
|
|
|
|
|
if not 'server_config' in existing_config:
|
|
|
|
|
|
existing_config['server_config'] = {}
|
|
|
|
|
|
|
|
|
|
|
|
existing_config['server_config']['hostname'] = hostname
|
|
|
|
|
|
print_success(f"服务器标识已设置为: {hostname}")
|
|
|
|
|
|
return existing_config
|
|
|
|
|
|
|
|
|
|
|
|
def test_mysql_connection(host, port, user, password, database=None):
|
|
|
|
|
|
"""测试MySQL连接"""
|
|
|
|
|
|
import pymysql
|
|
|
|
|
|
try:
|
|
|
|
|
|
conn_args = {
|
|
|
|
|
|
'host': host,
|
|
|
|
|
|
'port': port,
|
|
|
|
|
|
'user': user,
|
|
|
|
|
|
'password': password,
|
|
|
|
|
|
'connect_timeout': 5
|
|
|
|
|
|
}
|
|
|
|
|
|
if database:
|
|
|
|
|
|
conn_args['database'] = database
|
|
|
|
|
|
|
|
|
|
|
|
conn = pymysql.connect(**conn_args)
|
|
|
|
|
|
conn.close()
|
|
|
|
|
|
return True, None
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return False, str(e)
|
|
|
|
|
|
|
|
|
|
|
|
def configure_mysql_settings(existing_config):
|
|
|
|
|
|
"""配置MySQL设置"""
|
|
|
|
|
|
print_step(3, "配置MySQL数据库")
|
|
|
|
|
|
|
|
|
|
|
|
db_config = existing_config.get('database', {})
|
|
|
|
|
|
|
|
|
|
|
|
print("请输入MySQL配置信息:")
|
|
|
|
|
|
host = input(f"主机地址 (默认: {db_config.get('host', 'localhost')}): ").strip() or db_config.get('host', 'localhost')
|
|
|
|
|
|
port = input(f"端口 (默认: {db_config.get('port', 3306)}): ").strip() or db_config.get('port', 3306)
|
|
|
|
|
|
try:
|
|
|
|
|
|
port = int(port)
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
print_warning("端口必须是数字,使用默认值3306")
|
|
|
|
|
|
port = 3306
|
|
|
|
|
|
|
|
|
|
|
|
# 获取root信息以创建数据库和用户
|
|
|
|
|
|
print("\n需要MySQL root权限来创建数据库和用户:")
|
|
|
|
|
|
root_user = input("MySQL root用户名 (默认: root): ").strip() or "root"
|
|
|
|
|
|
root_password = getpass.getpass("MySQL root密码: ")
|
|
|
|
|
|
|
|
|
|
|
|
# 测试root连接
|
|
|
|
|
|
success, error_msg = test_mysql_connection(host, port, root_user, root_password)
|
|
|
|
|
|
if not success:
|
|
|
|
|
|
print_error(f"无法连接到MySQL: {error_msg}")
|
|
|
|
|
|
print_warning("请确认MySQL服务已启动且用户名密码正确")
|
|
|
|
|
|
retry = input("是否重试MySQL配置? (y/n): ").lower()
|
|
|
|
|
|
if retry == 'y':
|
|
|
|
|
|
return configure_mysql_settings(existing_config)
|
|
|
|
|
|
else:
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
print_success("MySQL连接测试成功")
|
|
|
|
|
|
|
|
|
|
|
|
# 应用数据库和用户设置
|
|
|
|
|
|
db_name = input(f"数据库名称 (默认: {db_config.get('database', 'auto_cursor_reg')}): ").strip() or db_config.get('database', 'auto_cursor_reg')
|
|
|
|
|
|
db_user = input(f"应用用户名 (默认: {db_config.get('username', 'auto_cursor_reg')}): ").strip() or db_config.get('username', 'auto_cursor_reg')
|
|
|
|
|
|
|
|
|
|
|
|
# 为应用用户生成密码
|
|
|
|
|
|
default_password = db_config.get('password')
|
|
|
|
|
|
if default_password:
|
|
|
|
|
|
use_existing = input(f"使用现有密码? (y/n, 默认: y): ").lower() != 'n'
|
|
|
|
|
|
if use_existing:
|
|
|
|
|
|
db_password = default_password
|
|
|
|
|
|
else:
|
|
|
|
|
|
suggested_password = generate_password(12)
|
|
|
|
|
|
db_password = input(f"应用用户密码 (建议: {suggested_password}): ").strip() or suggested_password
|
|
|
|
|
|
else:
|
|
|
|
|
|
suggested_password = generate_password(12)
|
|
|
|
|
|
db_password = input(f"应用用户密码 (建议: {suggested_password}): ").strip() or suggested_password
|
|
|
|
|
|
|
|
|
|
|
|
# 更新配置
|
|
|
|
|
|
existing_config['database'] = {
|
|
|
|
|
|
'host': host,
|
|
|
|
|
|
'port': port,
|
|
|
|
|
|
'username': db_user,
|
|
|
|
|
|
'password': db_password,
|
|
|
|
|
|
'database': db_name,
|
|
|
|
|
|
'pool_size': db_config.get('pool_size', 10),
|
|
|
|
|
|
'use_redis': db_config.get('use_redis', True)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 保存root信息用于创建数据库和用户
|
|
|
|
|
|
root_info = {
|
|
|
|
|
|
'host': host,
|
|
|
|
|
|
'port': port,
|
|
|
|
|
|
'user': root_user,
|
|
|
|
|
|
'password': root_password
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return existing_config, root_info
|
|
|
|
|
|
|
|
|
|
|
|
def test_redis_connection(host, port, password=None, db=0):
|
|
|
|
|
|
"""测试Redis连接"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
import redis
|
|
|
|
|
|
client = redis.Redis(
|
|
|
|
|
|
host=host,
|
|
|
|
|
|
port=port,
|
|
|
|
|
|
password=password if password else None,
|
|
|
|
|
|
db=db,
|
|
|
|
|
|
socket_timeout=5
|
|
|
|
|
|
)
|
|
|
|
|
|
client.ping()
|
|
|
|
|
|
client.close()
|
|
|
|
|
|
return True, None
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
return False, str(e)
|
|
|
|
|
|
|
|
|
|
|
|
def configure_redis_settings(existing_config):
|
|
|
|
|
|
"""配置Redis设置"""
|
|
|
|
|
|
print_step(4, "配置Redis缓存")
|
|
|
|
|
|
|
|
|
|
|
|
db_config = existing_config.get('database', {})
|
|
|
|
|
|
redis_config = existing_config.get('redis', {})
|
|
|
|
|
|
|
|
|
|
|
|
use_redis = input(f"是否使用Redis缓存? (y/n, 默认: {'y' if db_config.get('use_redis', True) else 'n'}): ").lower()
|
|
|
|
|
|
if use_redis == 'n':
|
|
|
|
|
|
existing_config['database']['use_redis'] = False
|
|
|
|
|
|
print_warning("Redis缓存已禁用")
|
|
|
|
|
|
return existing_config, None
|
|
|
|
|
|
|
|
|
|
|
|
existing_config['database']['use_redis'] = True
|
|
|
|
|
|
|
|
|
|
|
|
print("请输入Redis配置信息:")
|
|
|
|
|
|
host = input(f"主机地址 (默认: {redis_config.get('host', '127.0.0.1')}): ").strip() or redis_config.get('host', '127.0.0.1')
|
|
|
|
|
|
port = input(f"端口 (默认: {redis_config.get('port', 6379)}): ").strip() or redis_config.get('port', 6379)
|
|
|
|
|
|
try:
|
|
|
|
|
|
port = int(port)
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
print_warning("端口必须是数字,使用默认值6379")
|
|
|
|
|
|
port = 6379
|
|
|
|
|
|
|
|
|
|
|
|
password = input(f"密码 (默认: {'保持现有密码' if redis_config.get('password') else '无'}): ")
|
|
|
|
|
|
if not password and 'password' in redis_config:
|
|
|
|
|
|
password = redis_config['password']
|
|
|
|
|
|
|
|
|
|
|
|
db = input(f"数据库索引 (默认: {redis_config.get('db', 0)}): ").strip() or redis_config.get('db', 0)
|
|
|
|
|
|
try:
|
|
|
|
|
|
db = int(db)
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
print_warning("数据库索引必须是数字,使用默认值0")
|
|
|
|
|
|
db = 0
|
|
|
|
|
|
|
|
|
|
|
|
# 测试Redis连接
|
|
|
|
|
|
success, error_msg = test_redis_connection(host, port, password, db)
|
|
|
|
|
|
if not success:
|
|
|
|
|
|
print_error(f"无法连接到Redis: {error_msg}")
|
|
|
|
|
|
print_warning("请确认Redis服务已启动且配置正确")
|
|
|
|
|
|
retry = input("是否重试Redis配置? (y/n): ").lower()
|
|
|
|
|
|
if retry == 'y':
|
|
|
|
|
|
return configure_redis_settings(existing_config)
|
|
|
|
|
|
elif retry == 'n':
|
|
|
|
|
|
use_anyway = input("是否仍然使用这些设置? (y/n): ").lower()
|
|
|
|
|
|
if use_anyway != 'y':
|
|
|
|
|
|
existing_config['database']['use_redis'] = False
|
|
|
|
|
|
print_warning("Redis缓存已禁用")
|
|
|
|
|
|
return existing_config, None
|
|
|
|
|
|
else:
|
|
|
|
|
|
print_success("Redis连接测试成功")
|
|
|
|
|
|
|
|
|
|
|
|
# 更新配置
|
|
|
|
|
|
existing_config['redis'] = {
|
|
|
|
|
|
'host': host,
|
|
|
|
|
|
'port': port,
|
|
|
|
|
|
'password': password,
|
|
|
|
|
|
'db': db
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
redis_info = {
|
|
|
|
|
|
'host': host,
|
|
|
|
|
|
'port': port,
|
|
|
|
|
|
'password': password,
|
|
|
|
|
|
'db': db
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return existing_config, redis_info
|
|
|
|
|
|
|
|
|
|
|
|
def setup_mysql_database(config, root_info):
|
|
|
|
|
|
"""设置MySQL数据库和用户"""
|
|
|
|
|
|
print_step(5, "创建MySQL数据库和用户")
|
|
|
|
|
|
|
|
|
|
|
|
db_config = config['database']
|
|
|
|
|
|
db_name = db_config['database']
|
|
|
|
|
|
db_user = db_config['username']
|
|
|
|
|
|
db_password = db_config['password']
|
|
|
|
|
|
|
|
|
|
|
|
import pymysql
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 连接MySQL
|
|
|
|
|
|
conn = pymysql.connect(
|
|
|
|
|
|
host=root_info['host'],
|
|
|
|
|
|
port=root_info['port'],
|
|
|
|
|
|
user=root_info['user'],
|
|
|
|
|
|
password=root_info['password']
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
with conn.cursor() as cursor:
|
|
|
|
|
|
# 创建数据库
|
|
|
|
|
|
print(f"创建数据库 {db_name}...")
|
|
|
|
|
|
cursor.execute(f"CREATE DATABASE IF NOT EXISTS `{db_name}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")
|
|
|
|
|
|
|
|
|
|
|
|
# 创建用户并授权
|
|
|
|
|
|
print(f"创建用户 {db_user}...")
|
|
|
|
|
|
|
|
|
|
|
|
# 检查用户是否存在
|
|
|
|
|
|
cursor.execute(f"SELECT 1 FROM mysql.user WHERE user = %s AND host = %s", (db_user, '%'))
|
|
|
|
|
|
user_exists = cursor.fetchone()
|
|
|
|
|
|
|
|
|
|
|
|
if user_exists:
|
|
|
|
|
|
print_warning(f"用户 {db_user} 已存在,更新密码...")
|
|
|
|
|
|
cursor.execute(f"ALTER USER '{db_user}'@'%' IDENTIFIED BY %s", (db_password,))
|
|
|
|
|
|
else:
|
|
|
|
|
|
cursor.execute(f"CREATE USER '{db_user}'@'%' IDENTIFIED BY %s", (db_password,))
|
|
|
|
|
|
|
|
|
|
|
|
# 授权
|
|
|
|
|
|
cursor.execute(f"GRANT ALL PRIVILEGES ON `{db_name}`.* TO '{db_user}'@'%'")
|
|
|
|
|
|
cursor.execute("FLUSH PRIVILEGES")
|
|
|
|
|
|
|
|
|
|
|
|
conn.close()
|
|
|
|
|
|
print_success(f"数据库 {db_name} 和用户 {db_user} 创建成功")
|
|
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print_error(f"设置MySQL数据库失败: {str(e)}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def create_database_tables(config):
|
|
|
|
|
|
"""创建数据库表结构"""
|
|
|
|
|
|
print_step(6, "创建数据库表结构")
|
|
|
|
|
|
|
|
|
|
|
|
db_config = config['database']
|
|
|
|
|
|
|
|
|
|
|
|
import pymysql
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 连接MySQL
|
|
|
|
|
|
conn = pymysql.connect(
|
|
|
|
|
|
host=db_config['host'],
|
|
|
|
|
|
port=db_config['port'],
|
|
|
|
|
|
user=db_config['username'],
|
|
|
|
|
|
password=db_config['password'],
|
|
|
|
|
|
database=db_config['database']
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
with conn.cursor() as cursor:
|
|
|
|
|
|
# 创建email_accounts表
|
|
|
|
|
|
print("创建email_accounts表...")
|
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS email_accounts (
|
|
|
|
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
|
|
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
|
|
|
|
password VARCHAR(255) NOT NULL,
|
|
|
|
|
|
client_id VARCHAR(255) NOT NULL,
|
|
|
|
|
|
refresh_token TEXT NOT NULL,
|
|
|
|
|
|
in_use BOOLEAN DEFAULT 0,
|
|
|
|
|
|
cursor_password VARCHAR(255),
|
|
|
|
|
|
cursor_cookie TEXT,
|
|
|
|
|
|
cursor_token TEXT,
|
|
|
|
|
|
sold BOOLEAN DEFAULT 0,
|
|
|
|
|
|
status VARCHAR(20) DEFAULT 'pending',
|
|
|
|
|
|
extracted BOOLEAN DEFAULT 0,
|
|
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
|
|
INDEX idx_status_inuse_sold (status, in_use, sold),
|
|
|
|
|
|
INDEX idx_extracted (extracted, status, sold)
|
|
|
|
|
|
)
|
|
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
|
|
# 创建system_settings表
|
|
|
|
|
|
print("创建system_settings表...")
|
|
|
|
|
|
cursor.execute('''
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS system_settings (
|
|
|
|
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
|
|
|
|
key_name VARCHAR(50) UNIQUE NOT NULL,
|
|
|
|
|
|
value TEXT,
|
|
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
|
|
|
|
)
|
|
|
|
|
|
''')
|
|
|
|
|
|
|
|
|
|
|
|
# 初始化system_settings
|
|
|
|
|
|
cursor.execute(
|
|
|
|
|
|
"INSERT INTO system_settings (key_name, value) VALUES ('auto_service_enabled', '0') "
|
|
|
|
|
|
"ON DUPLICATE KEY UPDATE value = value"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
conn.close()
|
|
|
|
|
|
print_success("数据库表结构创建成功")
|
|
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print_error(f"创建数据库表失败: {str(e)}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def update_config_file(config):
|
|
|
|
|
|
"""更新配置文件"""
|
|
|
|
|
|
print_step(7, "更新配置文件")
|
|
|
|
|
|
|
|
|
|
|
|
config_path = "config.yaml"
|
|
|
|
|
|
backup_path = f"config.yaml.bak.{int(time.time())}"
|
|
|
|
|
|
|
|
|
|
|
|
# 备份现有配置
|
|
|
|
|
|
if os.path.exists(config_path):
|
|
|
|
|
|
try:
|
|
|
|
|
|
with open(config_path, 'r', encoding='utf-8') as src:
|
|
|
|
|
|
with open(backup_path, 'w', encoding='utf-8') as dst:
|
|
|
|
|
|
dst.write(src.read())
|
|
|
|
|
|
print_success(f"现有配置已备份为 {backup_path}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print_warning(f"备份配置文件失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
# 写入新配置
|
|
|
|
|
|
try:
|
|
|
|
|
|
with open(config_path, 'w', encoding='utf-8') as f:
|
|
|
|
|
|
yaml.dump(config, f, default_flow_style=False, sort_keys=False)
|
|
|
|
|
|
print_success(f"配置已更新至 {config_path}")
|
|
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print_error(f"更新配置文件失败: {str(e)}")
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def finalize_setup():
|
|
|
|
|
|
"""完成设置并提供指导"""
|
|
|
|
|
|
print_title("设置完成")
|
|
|
|
|
|
|
|
|
|
|
|
print(f"{Colors.GREEN}Cursor自动化服务环境配置已完成!{Colors.ENDC}")
|
|
|
|
|
|
print("\n接下来您可以:")
|
|
|
|
|
|
|
|
|
|
|
|
print(f"{Colors.BOLD}1. 导入邮箱账号:{Colors.ENDC}")
|
|
|
|
|
|
print(" python import_emails.py")
|
|
|
|
|
|
|
|
|
|
|
|
print(f"\n{Colors.BOLD}2. 启动服务:{Colors.ENDC}")
|
|
|
|
|
|
print(" python start.py")
|
|
|
|
|
|
print(" 或")
|
|
|
|
|
|
print(" python auto_cursor_service.py")
|
|
|
|
|
|
|
|
|
|
|
|
print(f"\n{Colors.BOLD}3. 设置为系统服务:{Colors.ENDC}")
|
|
|
|
|
|
print(" 请参考README文件中的系统服务设置说明")
|
|
|
|
|
|
|
|
|
|
|
|
print(f"\n{Colors.BOLD}如需帮助:{Colors.ENDC}")
|
|
|
|
|
|
print(" 查看各README文件获取详细使用说明")
|
|
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
"""主函数"""
|
|
|
|
|
|
print_title("Cursor自动化服务环境配置")
|
|
|
|
|
|
|
|
|
|
|
|
# 检查Python版本
|
|
|
|
|
|
if sys.version_info < (3, 7):
|
|
|
|
|
|
print_error("需要Python 3.7或更高版本")
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
# 检查并安装依赖
|
|
|
|
|
|
check_and_install_packages()
|
|
|
|
|
|
|
|
|
|
|
|
# 读取现有配置
|
|
|
|
|
|
existing_config = read_config()
|
|
|
|
|
|
|
|
|
|
|
|
# 配置服务器设置
|
|
|
|
|
|
config = configure_server_settings(existing_config)
|
|
|
|
|
|
|
|
|
|
|
|
# 配置MySQL
|
|
|
|
|
|
config, root_info = configure_mysql_settings(config)
|
|
|
|
|
|
|
|
|
|
|
|
# 配置Redis
|
|
|
|
|
|
config, redis_info = configure_redis_settings(config)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置MySQL数据库和用户
|
|
|
|
|
|
if not setup_mysql_database(config, root_info):
|
|
|
|
|
|
retry = input("MySQL设置失败,是否继续? (y/n): ").lower()
|
|
|
|
|
|
if retry != 'y':
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建数据库表
|
|
|
|
|
|
if not create_database_tables(config):
|
|
|
|
|
|
retry = input("创建表失败,是否继续? (y/n): ").lower()
|
|
|
|
|
|
if retry != 'y':
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
# 更新配置文件
|
|
|
|
|
|
if not update_config_file(config):
|
|
|
|
|
|
retry = input("更新配置文件失败,是否继续? (y/n): ").lower()
|
|
|
|
|
|
if retry != 'y':
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
# 完成设置
|
|
|
|
|
|
finalize_setup()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
try:
|
|
|
|
|
|
main()
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
|
print("\n\n设置已取消")
|
|
|
|
|
|
sys.exit(1)
|