Initial commit: 知识管理平台
主要功能: - 多用户管理系统 - 浏览器自动化(Playwright) - 任务编排和执行 - Docker容器化部署 - 数据持久化和日志管理 技术栈: - Flask 3.0.0 - Playwright 1.40.0 - SQLite with connection pooling - Docker + Docker Compose 部署说明详见README.md
This commit is contained in:
182
app_config.py
Executable file
182
app_config.py
Executable file
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
配置管理模块
|
||||
集中管理所有配置项,支持环境变量
|
||||
"""
|
||||
|
||||
import os
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
# 常量定义
|
||||
SECRET_KEY_FILE = 'data/secret_key.txt'
|
||||
|
||||
|
||||
def get_secret_key():
|
||||
"""获取SECRET_KEY(优先环境变量)"""
|
||||
# 优先从环境变量读取
|
||||
secret_key = os.environ.get('SECRET_KEY')
|
||||
if secret_key:
|
||||
return secret_key
|
||||
|
||||
# 从文件读取
|
||||
if os.path.exists(SECRET_KEY_FILE):
|
||||
with open(SECRET_KEY_FILE, 'r') as f:
|
||||
return f.read().strip()
|
||||
|
||||
# 生成新的
|
||||
new_key = os.urandom(24).hex()
|
||||
os.makedirs('data', exist_ok=True)
|
||||
with open(SECRET_KEY_FILE, 'w') as f:
|
||||
f.write(new_key)
|
||||
print(f"✓ 已生成新的SECRET_KEY并保存到 {SECRET_KEY_FILE}")
|
||||
return new_key
|
||||
|
||||
|
||||
class Config:
|
||||
"""应用配置基类"""
|
||||
|
||||
# ==================== Flask核心配置 ====================
|
||||
SECRET_KEY = get_secret_key()
|
||||
|
||||
# ==================== 会话安全配置 ====================
|
||||
SESSION_COOKIE_SECURE = os.environ.get('SESSION_COOKIE_SECURE', 'False').lower() == 'true'
|
||||
SESSION_COOKIE_HTTPONLY = True # 防止XSS攻击
|
||||
SESSION_COOKIE_SAMESITE = 'Lax' # 防止CSRF攻击
|
||||
PERMANENT_SESSION_LIFETIME = timedelta(hours=int(os.environ.get('SESSION_LIFETIME_HOURS', '24')))
|
||||
|
||||
# ==================== 数据库配置 ====================
|
||||
DB_FILE = os.environ.get('DB_FILE', 'data/app_data.db')
|
||||
DB_POOL_SIZE = int(os.environ.get('DB_POOL_SIZE', '5'))
|
||||
|
||||
# ==================== 浏览器配置 ====================
|
||||
SCREENSHOTS_DIR = os.environ.get('SCREENSHOTS_DIR', '截图')
|
||||
|
||||
# ==================== 并发控制配置 ====================
|
||||
MAX_CONCURRENT_GLOBAL = int(os.environ.get('MAX_CONCURRENT_GLOBAL', '2'))
|
||||
MAX_CONCURRENT_PER_ACCOUNT = int(os.environ.get('MAX_CONCURRENT_PER_ACCOUNT', '1'))
|
||||
|
||||
# ==================== 日志缓存配置 ====================
|
||||
MAX_LOGS_PER_USER = int(os.environ.get('MAX_LOGS_PER_USER', '100'))
|
||||
MAX_TOTAL_LOGS = int(os.environ.get('MAX_TOTAL_LOGS', '1000'))
|
||||
|
||||
# ==================== 验证码配置 ====================
|
||||
MAX_CAPTCHA_ATTEMPTS = int(os.environ.get('MAX_CAPTCHA_ATTEMPTS', '5'))
|
||||
CAPTCHA_EXPIRE_SECONDS = int(os.environ.get('CAPTCHA_EXPIRE_SECONDS', '300'))
|
||||
|
||||
# ==================== IP限流配置 ====================
|
||||
MAX_IP_ATTEMPTS_PER_HOUR = int(os.environ.get('MAX_IP_ATTEMPTS_PER_HOUR', '10'))
|
||||
IP_LOCK_DURATION = int(os.environ.get('IP_LOCK_DURATION', '3600')) # 秒
|
||||
|
||||
# ==================== 超时配置 ====================
|
||||
PAGE_LOAD_TIMEOUT = int(os.environ.get('PAGE_LOAD_TIMEOUT', '60000')) # 毫秒
|
||||
DEFAULT_TIMEOUT = int(os.environ.get('DEFAULT_TIMEOUT', '60000')) # 毫秒
|
||||
|
||||
# ==================== SocketIO配置 ====================
|
||||
SOCKETIO_CORS_ALLOWED_ORIGINS = os.environ.get('SOCKETIO_CORS_ALLOWED_ORIGINS', '*')
|
||||
|
||||
# ==================== 日志配置 ====================
|
||||
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
|
||||
LOG_FILE = os.environ.get('LOG_FILE', 'logs/app.log')
|
||||
LOG_MAX_BYTES = int(os.environ.get('LOG_MAX_BYTES', '10485760')) # 10MB
|
||||
LOG_BACKUP_COUNT = int(os.environ.get('LOG_BACKUP_COUNT', '5'))
|
||||
|
||||
# ==================== 安全配置 ====================
|
||||
DEBUG = os.environ.get('FLASK_DEBUG', 'False').lower() == 'true'
|
||||
ALLOWED_SCREENSHOT_EXTENSIONS = {'.png', '.jpg', '.jpeg'}
|
||||
MAX_SCREENSHOT_SIZE = int(os.environ.get('MAX_SCREENSHOT_SIZE', '10485760')) # 10MB
|
||||
|
||||
@classmethod
|
||||
def validate(cls):
|
||||
"""验证配置的有效性"""
|
||||
errors = []
|
||||
|
||||
# 验证SECRET_KEY
|
||||
if not cls.SECRET_KEY or len(cls.SECRET_KEY) < 32:
|
||||
errors.append("SECRET_KEY长度必须至少32个字符")
|
||||
|
||||
# 验证并发配置
|
||||
if cls.MAX_CONCURRENT_GLOBAL < 1:
|
||||
errors.append("MAX_CONCURRENT_GLOBAL必须大于0")
|
||||
|
||||
if cls.MAX_CONCURRENT_PER_ACCOUNT < 1:
|
||||
errors.append("MAX_CONCURRENT_PER_ACCOUNT必须大于0")
|
||||
|
||||
# 验证数据库配置
|
||||
if not cls.DB_FILE:
|
||||
errors.append("DB_FILE不能为空")
|
||||
|
||||
if cls.DB_POOL_SIZE < 1:
|
||||
errors.append("DB_POOL_SIZE必须大于0")
|
||||
|
||||
# 验证日志配置
|
||||
if cls.LOG_LEVEL not in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
|
||||
errors.append(f"LOG_LEVEL无效: {cls.LOG_LEVEL}")
|
||||
|
||||
return errors
|
||||
|
||||
@classmethod
|
||||
def print_config(cls):
|
||||
"""打印当前配置(隐藏敏感信息)"""
|
||||
print("=" * 60)
|
||||
print("应用配置")
|
||||
print("=" * 60)
|
||||
print(f"DEBUG模式: {cls.DEBUG}")
|
||||
print(f"SECRET_KEY: {'*' * 20} (长度: {len(cls.SECRET_KEY)})")
|
||||
print(f"会话超时: {cls.PERMANENT_SESSION_LIFETIME}")
|
||||
print(f"Cookie安全: HTTPS={cls.SESSION_COOKIE_SECURE}, HttpOnly={cls.SESSION_COOKIE_HTTPONLY}")
|
||||
print(f"数据库文件: {cls.DB_FILE}")
|
||||
print(f"数据库连接池: {cls.DB_POOL_SIZE}")
|
||||
print(f"并发配置: 全局={cls.MAX_CONCURRENT_GLOBAL}, 单账号={cls.MAX_CONCURRENT_PER_ACCOUNT}")
|
||||
print(f"日志级别: {cls.LOG_LEVEL}")
|
||||
print(f"日志文件: {cls.LOG_FILE}")
|
||||
print(f"截图目录: {cls.SCREENSHOTS_DIR}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
class DevelopmentConfig(Config):
|
||||
"""开发环境配置"""
|
||||
DEBUG = True
|
||||
SESSION_COOKIE_SECURE = False
|
||||
|
||||
|
||||
class ProductionConfig(Config):
|
||||
"""生产环境配置"""
|
||||
DEBUG = False
|
||||
SESSION_COOKIE_SECURE = True # 生产环境必须使用HTTPS
|
||||
|
||||
|
||||
class TestingConfig(Config):
|
||||
"""测试环境配置"""
|
||||
DEBUG = True
|
||||
TESTING = True
|
||||
DB_FILE = 'data/test_app_data.db'
|
||||
|
||||
|
||||
# 根据环境变量选择配置
|
||||
config_map = {
|
||||
'development': DevelopmentConfig,
|
||||
'production': ProductionConfig,
|
||||
'testing': TestingConfig,
|
||||
}
|
||||
|
||||
|
||||
def get_config():
|
||||
"""获取当前环境的配置"""
|
||||
env = os.environ.get('FLASK_ENV', 'production')
|
||||
return config_map.get(env, ProductionConfig)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 配置验证测试
|
||||
config = get_config()
|
||||
errors = config.validate()
|
||||
|
||||
if errors:
|
||||
print("配置验证失败:")
|
||||
for error in errors:
|
||||
print(f" ✗ {error}")
|
||||
else:
|
||||
print("✓ 配置验证通过")
|
||||
config.print_config()
|
||||
Reference in New Issue
Block a user