增加初始化安装引导交互

This commit is contained in:
huangzhenpc
2025-04-02 10:15:41 +08:00
parent d16f6bdc62
commit 5919b7e657
2 changed files with 662 additions and 0 deletions

137
SETUP_README.md Normal file
View File

@@ -0,0 +1,137 @@
# Cursor自动化服务 - 环境设置向导
这个交互式设置向导(`setup_environment.py`)用于在新服务器上快速配置Cursor自动化服务环境包括安装依赖、配置数据库和Redis、创建必要的表结构。
## 功能特点
1. **全自动化配置**
- 自动检测并安装必要的Python依赖
- 交互式配置MySQL和Redis
- 自动创建数据库和用户
- 创建必要的表结构
- 更新配置文件
2. **智能默认值**
- 使用主机名作为默认服务器标识
- 识别现有配置并提供作为默认值
- 为重要参数提供安全的建议值
3. **连接性测试**
- 验证MySQL连接是否可用
- 测试Redis连接
- 提供详细的错误反馈
## 使用方法
### 步骤1: 准备环境
确保服务器上已安装:
- Python 3.7+
- MySQL/MariaDB
- Redis (可选)
### 步骤2: 运行设置向导
```bash
python setup_environment.py
```
### 步骤3: 按照提示完成配置
设置向导将引导您完成以下步骤:
1. **检查并安装依赖**
- 自动安装必要的Python包
2. **配置服务器标识**
- 设置唯一的服务器标识用于API调用
3. **配置MySQL数据库**
- 输入MySQL服务器信息
- 提供MySQL root用户信息(用于创建数据库和用户)
- 设置应用程序数据库和用户
4. **配置Redis缓存(可选)**
- 选择是否使用Redis缓存
- 配置Redis连接信息
5. **创建数据库和用户**
- 自动创建数据库
- 创建应用用户并设置权限
6. **创建表结构**
- 创建email_accounts表
- 创建system_settings表
7. **更新配置文件**
- 备份现有配置
- 写入新配置到config.yaml
## 配置选项说明
### 服务器标识
服务器标识是一个唯一的名称用于在API调用中标识当前服务器。默认使用系统主机名。
### MySQL配置
- **主机地址**: MySQL服务器地址默认为`localhost`
- **端口**: MySQL服务端口默认为`3306`
- **数据库名**: 应用程序使用的数据库名,默认为`auto_cursor_reg`
- **应用用户名**: 应用程序使用的数据库用户,默认为`auto_cursor_reg`
- **应用用户密码**: 默认生成一个安全的随机密码
### Redis配置
- **是否启用**: 是否使用Redis缓存
- **主机地址**: Redis服务器地址默认为`127.0.0.1`
- **端口**: Redis服务端口默认为`6379`
- **密码**: Redis认证密码(如果设置了)
- **数据库索引**: Redis数据库索引默认为`0`
## 安全注意事项
- 本脚本需要MySQL root权限来创建数据库和用户
- 数据库密码会以明文保存在配置文件中
- 脚本会自动备份现有配置文件,以`.bak`扩展名保存
## 常见问题
1. **无法连接到MySQL**
- 确认MySQL服务已启动
- 验证root密码是否正确
- 检查防火墙设置
2. **无法连接到Redis**
- 确认Redis服务已启动
- 验证Redis密码是否正确
- 如不需要Redis可选择禁用
3. **权限问题**
- 确保使用的用户有创建数据库和用户的权限
4. **配置文件备份**
- 脚本会自动备份原始配置文件为`config.yaml.bak.[时间戳]`
## 完成后的步骤
设置完成后,您可以:
1. 导入邮箱账号:
```bash
python import_emails.py
```
2. 启动自动服务:
```bash
python start.py
```
3. 或直接启动自动化服务:
```bash
python auto_cursor_service.py
```
## 服务器系统要求
- **操作系统**: 支持Linux、Windows、macOS
- **内存**: 至少2GB RAM
- **存储**: 至少1GB可用空间
- **网络**: 稳定的互联网连接

525
setup_environment.py Normal file
View File

@@ -0,0 +1,525 @@
#!/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
# 尝试导入必要的库,如果不存在则安装
required_packages = [
"loguru",
"pymysql",
"aiomysql",
"redis",
"pyyaml"
]
# 设置颜色输出
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}")
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)