feat: migrate configuration to .env environment variables
This commit implements a complete migration from JSON-based configuration to .env environment variables, providing better security and flexibility. Key Changes: - Add python-dotenv dependency for environment variable support - Create config/env_loader.py with type conversion utilities - Add from_env() class methods to all config dataclasses - Update ConfigLoader to prioritize environment variables - Add save_to_env() method for .env file management - Implement database connection factory pattern - Add base DAO and connection classes for better abstraction - Support both SQL Server and MySQL with unified interface - Create migration script (scripts/migrate_to_env.py) - Update GUI to read/write .env files - Add comprehensive migration documentation New Files: - config/env_loader.py - Environment variable loader - db/base_connection.py - Base database connection interface - db/base_dao.py - Base DAO with common utilities - db/connection_factory.py - Factory for creating connections - db/mysql_connection.py - MySQL-specific connection - db/sqlserver_connection.py - SQL Server-specific connection - db/table_name_converter.py - SQL dialect converter - scripts/migrate_to_env.py - Configuration migration tool - docs/ENV_MIGRATION.md - Complete migration guide - .env.example - Environment variable template Testing: - Verified MySQL connection (8.0.44) - Tested all DAO operations - Confirmed 150 tables accessible - Validated configuration loading Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
226
scripts/migrate_to_env.py
Normal file
226
scripts/migrate_to_env.py
Normal file
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
配置迁移脚本
|
||||
|
||||
将现有的 JSON 配置文件迁移到 .env 环境变量文件
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到 sys.path
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from config.schema import AppConfig
|
||||
|
||||
|
||||
def migrate_json_to_env(
|
||||
json_file: str = "config/user_settings.json",
|
||||
env_file: str = ".env",
|
||||
backup: bool = True
|
||||
) -> bool:
|
||||
"""
|
||||
迁移 JSON 配置到 .env 文件
|
||||
|
||||
Args:
|
||||
json_file: JSON 配置文件路径
|
||||
env_file: .env 文件路径
|
||||
backup: 是否备份原 JSON 文件
|
||||
|
||||
Returns:
|
||||
迁移是否成功
|
||||
"""
|
||||
json_path = project_root / json_file
|
||||
env_path = project_root / env_file
|
||||
|
||||
# 检查 JSON 文件是否存在
|
||||
if not json_path.exists():
|
||||
print(f"❌ JSON 配置文件不存在: {json_path}")
|
||||
print(f"💡 提示: 如果这是首次运行,请复制 .env.example 到 .env 并填入配置")
|
||||
return False
|
||||
|
||||
# 检查 .env 文件是否已存在
|
||||
if env_path.exists():
|
||||
response = input(f"⚠️ .env 文件已存在: {env_path}\n是否覆盖? (y/N): ")
|
||||
if response.lower() != 'y':
|
||||
print("❌ 迁移已取消")
|
||||
return False
|
||||
|
||||
# 备份现有的 .env 文件
|
||||
backup_path = env_path.with_suffix(".env.backup")
|
||||
shutil.copy(env_path, backup_path)
|
||||
print(f"✅ 已备份现有 .env 文件到: {backup_path}")
|
||||
|
||||
try:
|
||||
# 读取 JSON 配置
|
||||
print(f"📖 读取 JSON 配置: {json_path}")
|
||||
with open(json_path, "r", encoding="utf-8") as f:
|
||||
json_data = json.load(f)
|
||||
|
||||
# 使用 ConfigLoader 将字典转换为配置对象
|
||||
from config.loader import ConfigLoader
|
||||
config = ConfigLoader._dict_to_config(json_data)
|
||||
|
||||
# 保存到 .env 文件
|
||||
print(f"💾 保存配置到 .env 文件: {env_path}")
|
||||
success = ConfigLoader.save_to_env(config, env_file)
|
||||
|
||||
if not success:
|
||||
print("❌ 保存 .env 文件失败")
|
||||
return False
|
||||
|
||||
# 备份原 JSON 文件
|
||||
if backup:
|
||||
backup_path = json_path.with_suffix(".json.backup")
|
||||
shutil.copy(json_path, backup_path)
|
||||
print(f"✅ 已备份 JSON 配置到: {backup_path}")
|
||||
|
||||
print("\n✅ 配置迁移成功!")
|
||||
print(f"\n📝 新配置文件: {env_path}")
|
||||
print(f"📦 备份文件: {backup_path if backup else '无'}")
|
||||
print("\n💡 提示:")
|
||||
print(" 1. 请检查 .env 文件中的配置是否正确")
|
||||
print(" 2. 确保 .env 文件不会被提交到版本控制")
|
||||
print(" 3. 可以删除原 JSON 配置文件: " + str(json_path))
|
||||
|
||||
return True
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"❌ JSON 解析失败: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 迁移失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def create_env_from_example(
|
||||
example_file: str = ".env.example",
|
||||
env_file: str = ".env"
|
||||
) -> bool:
|
||||
"""
|
||||
从 .env.example 创建 .env 文件
|
||||
|
||||
Args:
|
||||
example_file: .env.example 文件路径
|
||||
env_file: .env 文件路径
|
||||
|
||||
Returns:
|
||||
创建是否成功
|
||||
"""
|
||||
example_path = project_root / example_file
|
||||
env_path = project_root / env_file
|
||||
|
||||
if not example_path.exists():
|
||||
print(f"❌ .env.example 文件不存在: {example_path}")
|
||||
return False
|
||||
|
||||
if env_path.exists():
|
||||
response = input(f"⚠️ .env 文件已存在: {env_path}\n是否覆盖? (y/N): ")
|
||||
if response.lower() != 'y':
|
||||
print("❌ 操作已取消")
|
||||
return False
|
||||
|
||||
try:
|
||||
shutil.copy(example_path, env_path)
|
||||
print(f"✅ 已从 {example_file} 创建 {env_file}")
|
||||
print("\n💡 提示:")
|
||||
print(" 1. 请编辑 .env 文件,填入实际的配置值")
|
||||
print(" 2. 特别注意敏感信息(密码、密钥等)")
|
||||
print(" 3. 确保 .env 文件不会被提交到版本控制")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ 创建失败: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=" * 60)
|
||||
print("🔄 配置迁移工具 - JSON → .env")
|
||||
print("=" * 60)
|
||||
|
||||
# 检查命令行参数
|
||||
if len(sys.argv) > 1:
|
||||
command = sys.argv[1].lower()
|
||||
|
||||
if command == "from-example":
|
||||
# 从 .env.example 创建
|
||||
print("\n📋 模式: 从 .env.example 创建配置文件")
|
||||
example_file = sys.argv[2] if len(sys.argv) > 2 else ".env.example"
|
||||
env_file = sys.argv[3] if len(sys.argv) > 3 else ".env"
|
||||
create_env_from_example(example_file, env_file)
|
||||
return
|
||||
|
||||
elif command == "migrate":
|
||||
# 从 JSON 迁移
|
||||
print("\n📋 模式: 从 JSON 配置迁移")
|
||||
json_file = sys.argv[2] if len(sys.argv) > 2 else "config/user_settings.json"
|
||||
env_file = sys.argv[3] if len(sys.argv) > 3 else ".env"
|
||||
migrate_json_to_env(json_file, env_file)
|
||||
return
|
||||
|
||||
elif command == "help":
|
||||
print("""
|
||||
用法:
|
||||
python scripts/migrate_to_env.py <命令> [参数]
|
||||
|
||||
命令:
|
||||
migrate [json_file] [env_file] 从 JSON 配置迁移到 .env
|
||||
from-example [example] [env_file] 从 .env.example 创建配置文件
|
||||
help 显示此帮助信息
|
||||
|
||||
示例:
|
||||
python scripts/migrate_to_env.py migrate
|
||||
python scripts/migrate_to_env.py migrate config/user_settings.json .env
|
||||
python scripts/migrate_to_env.py from-example
|
||||
python scripts/migrate_to_env.py from-example .env.example .env.local
|
||||
""")
|
||||
return
|
||||
|
||||
# 交互模式
|
||||
print("\n请选择操作:")
|
||||
print(" 1. 从 JSON 配置迁移到 .env")
|
||||
print(" 2. 从 .env.example 创建配置文件")
|
||||
print(" 3. 退出")
|
||||
|
||||
choice = input("\n请输入选项 (1-3): ").strip()
|
||||
|
||||
if choice == "1":
|
||||
json_file = input("JSON 配置文件路径 (默认: config/user_settings.json): ").strip()
|
||||
if not json_file:
|
||||
json_file = "config/user_settings.json"
|
||||
|
||||
env_file = input(".env 文件路径 (默认: .env): ").strip()
|
||||
if not env_file:
|
||||
env_file = ".env"
|
||||
|
||||
backup_choice = input("是否备份原 JSON 文件? (Y/n): ").strip().lower()
|
||||
backup = backup_choice != 'n'
|
||||
|
||||
migrate_json_to_env(json_file, env_file, backup)
|
||||
|
||||
elif choice == "2":
|
||||
example_file = input(".env.example 文件路径 (默认: .env.example): ").strip()
|
||||
if not example_file:
|
||||
example_file = ".env.example"
|
||||
|
||||
env_file = input(".env 文件路径 (默认: .env): ").strip()
|
||||
if not env_file:
|
||||
env_file = ".env"
|
||||
|
||||
create_env_from_example(example_file, env_file)
|
||||
|
||||
elif choice == "3":
|
||||
print("👋 再见!")
|
||||
else:
|
||||
print("❌ 无效的选项")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user