216 lines
6.0 KiB
Python
216 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
环境变量配置和验证脚本
|
||
自动生成安全的随机密钥并验证配置
|
||
"""
|
||
import os
|
||
import sys
|
||
import secrets
|
||
import getpass
|
||
from pathlib import Path
|
||
|
||
|
||
def generate_secret_key(length=32):
|
||
"""生成安全的随机密钥"""
|
||
return secrets.token_urlsafe(length)
|
||
|
||
|
||
def generate_hex_key(length=32):
|
||
"""生成十六进制随机密钥"""
|
||
return secrets.token_hex(length)
|
||
|
||
|
||
def check_env_vars():
|
||
"""检查必需的环境变量"""
|
||
print("🔍 检查环境变量配置...")
|
||
print("-" * 60)
|
||
|
||
required_vars = {
|
||
'SECRET_KEY': '应用密钥(用于会话加密)',
|
||
'AUTH_SECRET_KEY': '认证密钥(用于API签名)',
|
||
'DATABASE_URL': '数据库连接URL'
|
||
}
|
||
|
||
missing_vars = []
|
||
weak_keys = []
|
||
|
||
for var_name, description in required_vars.items():
|
||
value = os.environ.get(var_name)
|
||
if not value:
|
||
print(f"❌ {var_name:20s} - 未设置 ({description})")
|
||
missing_vars.append(var_name)
|
||
else:
|
||
print(f"✅ {var_name:20s} - 已设置")
|
||
# 检查密钥强度
|
||
if 'KEY' in var_name:
|
||
if len(value) < 32:
|
||
print(f"⚠️ {var_name:20s} - 密钥长度不足32字符 (当前: {len(value)})")
|
||
weak_keys.append(var_name)
|
||
else:
|
||
print(f"✅ {var_name:20s} - 密钥长度符合要求")
|
||
|
||
print("-" * 60)
|
||
|
||
if missing_vars:
|
||
print(f"\n❌ 发现 {len(missing_vars)} 个未设置的环境变量")
|
||
return False
|
||
elif weak_keys:
|
||
print(f"\n⚠️ 发现 {len(weak_keys)} 个强度不足的密钥")
|
||
return False
|
||
else:
|
||
print("\n✅ 所有必需的环境变量已正确配置")
|
||
return True
|
||
|
||
|
||
def generate_env_file():
|
||
"""生成 .env 文件"""
|
||
print("\n🚀 生成 .env 文件...")
|
||
print("-" * 60)
|
||
|
||
# 收集用户输入
|
||
database_url = input("数据库URL (mysql://user:pass@localhost:3306/dbname): ").strip()
|
||
if not database_url:
|
||
print("❌ 数据库URL不能为空")
|
||
return False
|
||
|
||
frontend_domain = input("前端域名 (your-domain.com): ").strip()
|
||
admin_email = input("管理员邮箱 (admin@yourcompany.com): ").strip()
|
||
|
||
# 生成随机密钥
|
||
secret_key = generate_secret_key(32)
|
||
auth_secret_key = generate_secret_key(32)
|
||
|
||
# 生成 .env 文件内容
|
||
env_content = f"""# KaMiXiTong 生产环境配置
|
||
# 生成时间: {__import__('datetime').datetime.now().isoformat()}
|
||
|
||
# ==========================================
|
||
# 🔐 安全配置
|
||
# ==========================================
|
||
SECRET_KEY={secret_key}
|
||
AUTH_SECRET_KEY={auth_secret_key}
|
||
|
||
# ==========================================
|
||
# 🗄️ 数据库配置
|
||
# ==========================================
|
||
DATABASE_URL={database_url}
|
||
DB_POOL_SIZE=20
|
||
DB_MAX_OVERFLOW=30
|
||
DB_POOL_RECYCLE=3600
|
||
DB_POOL_TIMEOUT=30
|
||
|
||
# ==========================================
|
||
# 🌐 系统配置
|
||
# ==========================================
|
||
SITE_NAME=KaMiXiTong软件授权管理系统
|
||
ADMIN_EMAIL={admin_email}
|
||
FRONTEND_DOMAIN={frontend_domain}
|
||
API_VERSION=v1
|
||
|
||
# ==========================================
|
||
# 🔒 安全策略
|
||
# ==========================================
|
||
SESSION_COOKIE_SECURE=true
|
||
SESSION_COOKIE_HTTPONLY=true
|
||
SESSION_COOKIE_SAMESITE=Lax
|
||
SESSION_LIFETIME_HOURS=24
|
||
REMEMBER_COOKIE_DURATION=30
|
||
REMEMBER_COOKIE_SECURE=true
|
||
MAX_FAILED_ATTEMPTS=5
|
||
LOCKOUT_MINUTES=10
|
||
MAX_UNBIND_TIMES=3
|
||
|
||
# ==========================================
|
||
# 💾 缓存配置
|
||
# ==========================================
|
||
REDIS_URL=redis://localhost:6379/0
|
||
|
||
# ==========================================
|
||
# 📝 日志配置
|
||
# ==========================================
|
||
LOG_LEVEL=INFO
|
||
LOG_FILE=logs/kamaxitong.log
|
||
|
||
# ==========================================
|
||
# 📦 文件上传
|
||
# ==========================================
|
||
MAX_CONTENT_LENGTH=52428800
|
||
UPLOAD_FOLDER=static/uploads
|
||
|
||
# ==========================================
|
||
# 🏷️ 卡密配置
|
||
# ==========================================
|
||
LICENSE_KEY_LENGTH=32
|
||
TRIAL_PREFIX=TRIAL_
|
||
OFFLINE_CACHE_DAYS=7
|
||
|
||
# ==========================================
|
||
# 🌍 环境标识
|
||
# ==========================================
|
||
FLASK_ENV=production
|
||
DEBUG=false
|
||
|
||
# ==========================================
|
||
# 💳 支付配置(可选)
|
||
# ==========================================
|
||
PAYMENT_ENABLED=false
|
||
"""
|
||
|
||
# 写入文件
|
||
env_file = Path('.env')
|
||
try:
|
||
with open(env_file, 'w', encoding='utf-8') as f:
|
||
f.write(env_content)
|
||
print(f"✅ .env 文件已生成: {env_file.absolute()}")
|
||
print(f"\n⚠️ 请妥善保管此文件,不要提交到版本控制系统")
|
||
return True
|
||
except Exception as e:
|
||
print(f"❌ 生成 .env 文件失败: {str(e)}")
|
||
return False
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
print("=" * 60)
|
||
print("🔧 KaMiXiTong 环境变量配置工具")
|
||
print("=" * 60)
|
||
|
||
# 检查是否已存在 .env 文件
|
||
env_file = Path('.env')
|
||
if env_file.exists():
|
||
print(f"\n⚠️ 发现已存在的 .env 文件")
|
||
overwrite = input("是否覆盖?(y/N): ").strip().lower()
|
||
if overwrite != 'y':
|
||
print("操作已取消")
|
||
return
|
||
|
||
# 生成 .env 文件
|
||
if not generate_env_file():
|
||
sys.exit(1)
|
||
|
||
# 重新加载环境变量
|
||
print("\n🔄 重新加载环境变量...")
|
||
from dotenv import load_dotenv
|
||
load_dotenv()
|
||
|
||
# 验证配置
|
||
if not check_env_vars():
|
||
print("\n❌ 环境变量配置不完整,请检查后重新运行")
|
||
sys.exit(1)
|
||
|
||
print("\n" + "=" * 60)
|
||
print("✅ 环境变量配置完成!")
|
||
print("=" * 60)
|
||
print("\n📋 下一步操作:")
|
||
print("1. 运行数据库迁移: flask db upgrade")
|
||
print("2. 启动应用: flask run")
|
||
print("3. 访问健康检查: curl http://localhost:5000/api/v1/health")
|
||
print("\n🔐 安全提醒:")
|
||
print("- 妥善保管 .env 文件,不要提交到版本控制")
|
||
print("- 定期更换密钥")
|
||
print("- 确保生产环境启用HTTPS")
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|