From 0ce2d2eab6fe413f6ac8e671c37794e1b5b83576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B3=A2=E6=BE=9C=E5=A3=AE=E9=98=94?= <263303411@qq.com> Date: Sun, 17 Aug 2025 21:25:55 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9B=86=E6=88=90=E7=94=9F=E6=88=90=E5=92=8C?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E7=8E=AF=E5=A2=83=E5=88=86=E5=88=AB=E7=9A=84?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 138 +++++- .gitignore | 3 + .../__pycache__/_textwrap.cpython-312.pyc | Bin 2413 -> 2413 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 207 -> 207 bytes .../__pycache__/debughelpers.cpython-312.pyc | Bin 9092 -> 9098 bytes Dockerfile | 45 ++ config/README.md | 184 ++++++++ config/__init__.py | 34 ++ config/base.py | 92 ++++ config/development.py | 53 +++ config/production.py | 69 +++ config/testing.py | 64 +++ deploy.sh | 159 +++++++ docker-compose.yml | 81 ++++ docker/mysql/init.sql | 26 ++ docker/nginx/nginx.conf | 137 ++++++ docs/development/Windows生产环境部署指南.md | 205 +++++++++ docs/development/部署配置优化总结.md | 408 ++++++++++++++++++ docs/development/配置系统重构总结.md | 306 +++++++++++++ docs/development/配置迁移指南.md | 223 ++++++++++ env.example | 136 ++++++ env.production | 57 +++ env.test | 40 ++ gunicorn.conf.py | 74 ++++ requirements.txt | 11 +- run_production_simple.py | 50 +++ src/flask_prompt_master/__init__.py | 30 +- src/flask_prompt_master/routes/routes.py | 393 +++++++++++++++-- start.bat | 2 - start.sh | 2 - start_production.bat | 19 + start_production_simple.bat | 22 + stop.bat | 2 - stop.sh | 2 - stop_production.bat | 18 + tests/conftest.py | 106 ++++- uwsgi.ini | 28 +- 37 files changed, 3139 insertions(+), 80 deletions(-) create mode 100644 Dockerfile create mode 100644 config/README.md create mode 100644 config/__init__.py create mode 100644 config/base.py create mode 100644 config/development.py create mode 100644 config/production.py create mode 100644 config/testing.py create mode 100644 deploy.sh create mode 100644 docker-compose.yml create mode 100644 docker/mysql/init.sql create mode 100644 docker/nginx/nginx.conf create mode 100644 docs/development/Windows生产环境部署指南.md create mode 100644 docs/development/部署配置优化总结.md create mode 100644 docs/development/配置系统重构总结.md create mode 100644 docs/development/配置迁移指南.md create mode 100644 env.example create mode 100644 env.production create mode 100644 env.test create mode 100644 gunicorn.conf.py create mode 100644 run_production_simple.py delete mode 100644 start.bat delete mode 100644 start.sh create mode 100644 start_production.bat create mode 100644 start_production_simple.bat delete mode 100644 stop.bat delete mode 100644 stop.sh create mode 100644 stop_production.bat diff --git a/.env b/.env index b89e84c..71000b5 100644 --- a/.env +++ b/.env @@ -1,2 +1,136 @@ -WX_APPID=wx2c65877d37fc29bf -WX_SECRET=89aa97dda3c1347c6ae3d6ab4627f1f4 \ No newline at end of file +# ======================================== +# Flask提示词大师应用环境变量配置示例 +# ======================================== +# 复制此文件为 .env 并根据实际情况修改配置 +# cp env.example .env + +# ======================================== +# Flask基础配置 +# ======================================== +# Flask应用密钥(必需) +SECRET_KEY=your-secret-key-here + +# 应用环境(development/production/testing/local) +FLASK_ENV=development + +# ======================================== +# 数据库配置 +# ======================================== +# 数据库连接URL(必需) +# MySQL示例: mysql+pymysql://username:password@localhost:3306/database_name?charset=utf8mb4 +# SQLite示例: sqlite:///app.db +# PostgreSQL示例: postgresql://username:password@localhost:5432/database_name +DATABASE_URL=mysql+pymysql://root:123456@localhost:3306/pro_db?charset=utf8mb4 + +# ======================================== +# OpenAI兼容API配置 +# ======================================== +# API基础URL(必需) +LLM_API_URL=https://api.deepseek.com/v1 + +# API密钥(必需) +LLM_API_KEY=sk-your-api-key-here + +# ======================================== +# 微信小程序配置 +# ======================================== +# 小程序AppID(必需) +WX_APPID=wx-your-appid-here + +# 小程序Secret(必需) +WX_SECRET=your-wx-secret-here + +# ======================================== +# 跨域配置 +# ======================================== +# 允许跨域的域名,多个用逗号分隔 +# 开发环境: http://localhost:3000,http://127.0.0.1:3000 +# 生产环境: https://yourdomain.com,https://www.yourdomain.com +CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000 + +# ======================================== +# 日志配置 +# ======================================== +# 日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL) +LOG_LEVEL=INFO + +# 日志文件路径 +LOG_FILE=logs/app.log + +# ======================================== +# 缓存配置 +# ======================================== +# 缓存类型(simple/redis/memcached) +CACHE_TYPE=simple + +# 缓存默认超时时间(秒) +CACHE_DEFAULT_TIMEOUT=300 + +# Redis缓存URL(当CACHE_TYPE=redis时使用) +# REDIS_URL=redis://localhost:6379/0 + +# ======================================== +# 会话配置 +# ======================================== +# 会话生命周期(小时) +SESSION_LIFETIME_HOURS=24 + +# ======================================== +# 文件上传配置 +# ======================================== +# 最大文件上传大小(字节) +MAX_CONTENT_LENGTH=16777216 + +# 文件上传目录 +UPLOAD_FOLDER=uploads + +# ======================================== +# 安全配置 +# ======================================== +# 是否启用CSRF保护 +WTF_CSRF_ENABLED=True + +# CSRF令牌超时时间(秒) +WTF_CSRF_TIME_LIMIT=3600 + +# ======================================== +# 邮件配置(生产环境错误报告) +# ======================================== +# 邮件服务器地址 +# MAIL_SERVER=smtp.gmail.com + +# 邮件服务器端口 +# MAIL_PORT=587 + +# 发件人邮箱 +# MAIL_FROM=noreply@yourdomain.com + +# 管理员邮箱(多个用逗号分隔) +# ADMIN_EMAIL=admin@yourdomain.com + +# ======================================== +# 性能配置 +# ======================================== +# 数据库连接池大小 +# DB_POOL_SIZE=20 + +# 数据库连接池最大溢出连接数 +# DB_MAX_OVERFLOW=30 + +# ======================================== +# 开发工具配置 +# ======================================== +# 是否启用自动重载 +# FLASK_DEBUG=True + +# 是否启用详细错误页面 +# FLASK_DEBUG_TB_ENABLED=True + +# ======================================== +# 监控配置 +# ======================================== +# 是否启用性能监控 +# ENABLE_MONITORING=False + +# 监控数据收集间隔(秒) +# MONITORING_INTERVAL=60 diff --git a/.gitignore b/.gitignore index bafa384..e31094b 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,9 @@ ENV/ env.bak/ venv.bak/ +# 本地配置文件 +config/local.py + # Spyder project settings .spyderproject .spyproject diff --git a/.venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-312.pyc b/.venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-312.pyc index 758cb7ef369f863b631606931d8e332d7f5a5356..2da3b9ddf692417956154a3628ba1918fa1b068e 100644 GIT binary patch delta 47 zcmaDW^j3)XG%qg~0}wpkwJ@Vk;$ C^$G%qg~0}wpkwJ<|*B5wk3id9T`MMZK&Ok!q9@x&^5#+Zq-OaU#M4xa!3 delta 43 xcmX@lc%G5>G%qg~0}yy!UXd<0kvDrldGId1943qyEHMrT`nE4bcDq diff --git a/.venv/Lib/site-packages/flask/__pycache__/debughelpers.cpython-312.pyc b/.venv/Lib/site-packages/flask/__pycache__/debughelpers.cpython-312.pyc index 46527e267c2dfc1b0c65f8cf066a03cab4949d80..3dacaef8c5ad1cd1a8ab3664f7d6e7ea8164b4af 100644 GIT binary patch delta 35 pcmZp1?{eop&CAQh00fVBE!@amz{F|eVii+fQITA+xsHiV5dgjB3cdgU delta 29 jcmeBjZ*k{7&CAQh00jRe7Hs4$U}83NvD)0i#HI)UcMS)U diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4e0e338 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# 使用Python 3.9官方镜像作为基础镜像 +FROM python:3.9-slim + +# 设置工作目录 +WORKDIR /app + +# 设置环境变量 +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + FLASK_ENV=production \ + FLASK_APP=run_dev.py + +# 安装系统依赖 +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + gcc \ + g++ \ + libpq-dev \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# 复制requirements文件 +COPY requirements.txt . + +# 安装Python依赖 +RUN pip install --no-cache-dir -r requirements.txt + +# 复制项目文件 +COPY . . + +# 创建必要的目录 +RUN mkdir -p logs uploads + +# 设置权限 +RUN chmod +x start.sh stop.sh + +# 暴露端口 +EXPOSE 5000 + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:5000/health || exit 1 + +# 启动命令 +CMD ["gunicorn", "--config", "gunicorn.conf.py", "run_dev:app"] diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..5cca98e --- /dev/null +++ b/config/README.md @@ -0,0 +1,184 @@ +# 配置管理说明 + +## 概述 + +本项目采用分层配置管理,支持多环境部署。配置系统基于Flask的配置机制,通过环境变量和配置文件实现灵活的配置管理。 + +## 配置结构 + +``` +config/ +├── __init__.py # 配置工厂和映射 +├── base.py # 基础配置类 +├── development.py # 开发环境配置 +├── production.py # 生产环境配置 +├── testing.py # 测试环境配置 +└── local.py # 本地配置(git忽略) +``` + +## 环境变量文件 + +``` +env.example # 环境变量示例 +env.test # 测试环境变量 +env.production # 生产环境变量 +.env # 本地环境变量(git忽略) +``` + +## 使用方法 + +### 1. 设置环境变量 + +复制环境变量示例文件: +```bash +cp env.example .env +``` + +编辑 `.env` 文件,设置实际的环境变量值。 + +### 2. 在应用中使用配置 + +```python +from config import get_config + +# 获取当前环境的配置类 +config_class = get_config() + +# 在Flask应用中使用 +app.config.from_object(config_class) +``` + +### 3. 切换环境 + +通过设置 `FLASK_ENV` 环境变量切换环境: + +```bash +# 开发环境 +export FLASK_ENV=development + +# 生产环境 +export FLASK_ENV=production + +# 测试环境 +export FLASK_ENV=testing + +# 本地环境 +export FLASK_ENV=local +``` + +## 配置项说明 + +### 必需配置项 + +- `SECRET_KEY`: Flask应用密钥 +- `DATABASE_URL`: 数据库连接URL +- `LLM_API_URL`: OpenAI兼容API基础URL +- `LLM_API_KEY`: OpenAI兼容API密钥 +- `WX_APPID`: 微信小程序AppID +- `WX_SECRET`: 微信小程序Secret + +### 可选配置项 + +- `FLASK_ENV`: 应用环境(默认:development) +- `CORS_ORIGINS`: 跨域域名(默认:*) +- `LOG_LEVEL`: 日志级别(默认:INFO) +- `CACHE_TYPE`: 缓存类型(默认:simple) +- `SESSION_LIFETIME_HOURS`: 会话生命周期(默认:24小时) + +## 环境特定配置 + +### 开发环境 (development) +- 启用调试模式 +- 使用SQLite数据库(如果未设置DATABASE_URL) +- 关闭CSRF保护 +- 详细的日志输出 + +### 生产环境 (production) +- 关闭调试模式 +- 启用CSRF保护 +- 使用Redis缓存 +- 邮件错误报告 +- 严格的跨域控制 + +### 测试环境 (testing) +- 使用内存数据库 +- 关闭CSRF保护 +- 使用测试密钥 +- 最短的缓存时间 + +### 本地环境 (local) +- 继承开发环境配置 +- 可自定义本地特定设置 +- 不会被提交到版本控制 + +## 安全注意事项 + +1. **敏感信息保护** + - 不要在代码中硬编码敏感信息 + - 使用环境变量管理所有敏感配置 + - 确保 `.env` 文件不被提交到版本控制 + +2. **生产环境安全** + - 使用强密码和密钥 + - 设置具体的跨域域名 + - 启用所有安全功能 + +3. **配置验证** + - 应用启动时会验证必需配置项 + - 缺少必需配置项会抛出异常 + +## 最佳实践 + +1. **环境分离** + - 不同环境使用不同的配置 + - 避免在代码中硬编码环境特定配置 + +2. **配置文档** + - 及时更新配置说明 + - 记录配置项的用途和影响 + +3. **配置测试** + - 在测试环境中验证配置 + - 确保配置变更不会影响功能 + +4. **配置备份** + - 备份重要的配置文件 + - 记录配置变更历史 + +## 故障排除 + +### 常见问题 + +1. **配置加载失败** + - 检查环境变量是否正确设置 + - 确认配置文件路径正确 + +2. **敏感信息泄露** + - 检查是否有硬编码的敏感信息 + - 确认 `.env` 文件在 `.gitignore` 中 + +3. **环境切换失败** + - 检查 `FLASK_ENV` 环境变量 + - 确认对应的配置类存在 + +### 调试技巧 + +1. **查看当前配置** + ```python + from config import get_config + config = get_config() + print(config.__dict__) + ``` + +2. **检查环境变量** + ```python + import os + print(os.environ.get('FLASK_ENV')) + ``` + +3. **验证配置项** + ```python + # 在应用启动时检查配置 + app.logger.info(f"当前环境: {app.config['ENV']}") + app.logger.info(f"调试模式: {app.config['DEBUG']}") + ``` diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..2e1607c --- /dev/null +++ b/config/__init__.py @@ -0,0 +1,34 @@ +import os +from .base import Config +from .development import DevelopmentConfig +from .production import ProductionConfig +from .testing import TestingConfig +from .local import LocalConfig + +# 配置映射字典 +config_map = { + 'development': DevelopmentConfig, + 'production': ProductionConfig, + 'testing': TestingConfig, + 'local': LocalConfig, + 'default': DevelopmentConfig +} + +def get_config(): + """ + 根据环境变量获取对应的配置类 + 环境变量: FLASK_ENV + 可选值: development, production, testing, local + """ + env = os.environ.get('FLASK_ENV', 'development') + return config_map.get(env, config_map['default']) + +# 导出配置类 +__all__ = [ + 'Config', + 'DevelopmentConfig', + 'ProductionConfig', + 'TestingConfig', + 'LocalConfig', + 'get_config' +] diff --git a/config/base.py b/config/base.py new file mode 100644 index 0000000..8f08afd --- /dev/null +++ b/config/base.py @@ -0,0 +1,92 @@ +import os +from datetime import timedelta + +class Config: + """ + 基础配置类 + 包含所有环境通用的配置项 + """ + + # Flask基础配置 + SECRET_KEY = os.environ.get('SECRET_KEY') + if not SECRET_KEY: + raise ValueError("SECRET_KEY 环境变量未设置") + + # 数据库配置 + SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') + + SQLALCHEMY_TRACK_MODIFICATIONS = False + SQLALCHEMY_ENGINE_OPTIONS = { + 'pool_pre_ping': True, + 'pool_recycle': 300, + } + + # OpenAI兼容API配置 + LLM_API_URL = os.environ.get('LLM_API_URL') + if not LLM_API_URL: + raise ValueError("LLM_API_URL 环境变量未设置") + + LLM_API_KEY = os.environ.get('LLM_API_KEY') + if not LLM_API_KEY: + raise ValueError("LLM_API_KEY 环境变量未设置") + + # 微信小程序配置 + WX_APPID = os.environ.get('WX_APPID') + if not WX_APPID: + raise ValueError("WX_APPID 环境变量未设置") + + WX_SECRET = os.environ.get('WX_SECRET') + if not WX_SECRET: + raise ValueError("WX_SECRET 环境变量未设置") + + # 跨域配置 + CORS_ORIGINS = os.environ.get('CORS_ORIGINS', '*').split(',') + + # 日志配置 + LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO') + LOG_FILE = os.environ.get('LOG_FILE', 'logs/app.log') + + # 缓存配置 + CACHE_TYPE = os.environ.get('CACHE_TYPE', 'simple') + CACHE_DEFAULT_TIMEOUT = int(os.environ.get('CACHE_DEFAULT_TIMEOUT', 300)) + + # 会话配置 + PERMANENT_SESSION_LIFETIME = timedelta( + hours=int(os.environ.get('SESSION_LIFETIME_HOURS', 24)) + ) + + # 文件上传配置 + MAX_CONTENT_LENGTH = int(os.environ.get('MAX_CONTENT_LENGTH', 16 * 1024 * 1024)) # 16MB + UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER', 'uploads') + + # 安全配置 + WTF_CSRF_ENABLED = os.environ.get('WTF_CSRF_ENABLED', 'True').lower() == 'true' + WTF_CSRF_TIME_LIMIT = int(os.environ.get('WTF_CSRF_TIME_LIMIT', 3600)) + + @staticmethod + def init_app(app): + """ + 初始化应用配置 + """ + # 创建必要的目录 + os.makedirs('logs', exist_ok=True) + os.makedirs('uploads', exist_ok=True) + + # 配置日志 + import logging + from logging.handlers import RotatingFileHandler + + if not app.debug and not app.testing: + file_handler = RotatingFileHandler( + Config.LOG_FILE, + maxBytes=10240000, + backupCount=10 + ) + file_handler.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' + )) + file_handler.setLevel(logging.INFO) + app.logger.addHandler(file_handler) + + app.logger.setLevel(logging.INFO) + app.logger.info('应用启动') diff --git a/config/development.py b/config/development.py new file mode 100644 index 0000000..e2b1129 --- /dev/null +++ b/config/development.py @@ -0,0 +1,53 @@ +from .base import Config + +class DevelopmentConfig(Config): + """ + 开发环境配置 + """ + DEBUG = True + TESTING = False + + # 开发环境数据库配置(如果未设置环境变量,使用SQLite) + def __init__(self): + super().__init__() + if not self.SQLALCHEMY_DATABASE_URI: + self.SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db' + + # 开发环境日志配置 + LOG_LEVEL = 'DEBUG' + LOG_FILE = 'logs/dev.log' + + # 开发环境缓存配置 + CACHE_TYPE = 'simple' + CACHE_DEFAULT_TIMEOUT = 60 # 开发环境缓存时间较短 + + # 开发环境安全配置 + WTF_CSRF_ENABLED = False # 开发环境关闭CSRF保护 + + # 开发环境会话配置 + SESSION_LIFETIME_HOURS = 24 + + # 开发环境文件上传配置 + MAX_CONTENT_LENGTH = 32 * 1024 * 1024 # 32MB + UPLOAD_FOLDER = 'uploads/dev' + + # 开发环境跨域配置 + CORS_ORIGINS = ['http://localhost:3000', 'http://127.0.0.1:3000', '*'] + + @staticmethod + def init_app(app): + Config.init_app(app) + + # 开发环境特定初始化 + import logging + app.logger.setLevel(logging.DEBUG) + + # 开发环境控制台输出 + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.DEBUG) + console_handler.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s: %(message)s' + )) + app.logger.addHandler(console_handler) + + app.logger.info('开发环境启动') diff --git a/config/production.py b/config/production.py new file mode 100644 index 0000000..c40ff8d --- /dev/null +++ b/config/production.py @@ -0,0 +1,69 @@ +import os +from .base import Config + +class ProductionConfig(Config): + """ + 生产环境配置 + """ + DEBUG = False + TESTING = False + + # 生产环境日志配置 + LOG_LEVEL = 'WARNING' + LOG_FILE = 'logs/production.log' + + # 生产环境缓存配置 + CACHE_TYPE = 'redis' + CACHE_REDIS_URL = os.environ.get('REDIS_URL') + CACHE_DEFAULT_TIMEOUT = 3600 # 1小时 + + # 生产环境安全配置 + WTF_CSRF_ENABLED = True + WTF_CSRF_TIME_LIMIT = 3600 + + # 生产环境会话配置 + SESSION_LIFETIME_HOURS = 168 # 7天 + + # 生产环境文件上传配置 + MAX_CONTENT_LENGTH = 8 * 1024 * 1024 # 8MB + UPLOAD_FOLDER = 'uploads/production' + + # 生产环境跨域配置(需要设置具体的域名) + CORS_ORIGINS = os.environ.get('CORS_ORIGINS', '').split(',') + if not CORS_ORIGINS or CORS_ORIGINS == ['']: + # 如果没有设置,使用默认值而不是抛出异常 + CORS_ORIGINS = ['https://yourdomain.com'] + + # 生产环境性能配置 + SQLALCHEMY_ENGINE_OPTIONS = { + 'pool_pre_ping': True, + 'pool_recycle': 300, + 'pool_size': 20, + 'max_overflow': 30, + } + + @staticmethod + def init_app(app): + Config.init_app(app) + + # 生产环境特定初始化 + import logging + app.logger.setLevel(logging.WARNING) + + # 生产环境错误处理 + if not app.debug and not app.testing: + import logging + from logging.handlers import SMTPHandler + + # 邮件错误报告(如果配置了邮件服务器) + mail_handler = SMTPHandler( + mailhost=(os.environ.get('MAIL_SERVER', 'localhost'), + int(os.environ.get('MAIL_PORT', 25))), + fromaddr=os.environ.get('MAIL_FROM', 'noreply@example.com'), + toaddrs=os.environ.get('ADMIN_EMAIL', '').split(','), + subject='应用错误报告' + ) + mail_handler.setLevel(logging.ERROR) + app.logger.addHandler(mail_handler) + + app.logger.info('生产环境启动') diff --git a/config/testing.py b/config/testing.py new file mode 100644 index 0000000..bf56b4e --- /dev/null +++ b/config/testing.py @@ -0,0 +1,64 @@ +import os +import tempfile +from .base import Config + +class TestingConfig(Config): + """ + 测试环境配置 + """ + DEBUG = False + TESTING = True + + # 测试环境数据库配置(使用内存数据库) + SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' + + # 测试环境日志配置 + LOG_LEVEL = 'DEBUG' + LOG_FILE = 'logs/test.log' + + # 测试环境缓存配置 + CACHE_TYPE = 'simple' + CACHE_DEFAULT_TIMEOUT = 10 # 测试环境缓存时间很短 + + # 测试环境安全配置 + WTF_CSRF_ENABLED = False # 测试环境关闭CSRF保护 + WTF_CSRF_TIME_LIMIT = 300 + + # 测试环境会话配置 + SESSION_LIFETIME_HOURS = 1 + + # 测试环境文件上传配置 + MAX_CONTENT_LENGTH = 1 * 1024 * 1024 # 1MB + UPLOAD_FOLDER = tempfile.mkdtemp() + + # 测试环境跨域配置 + CORS_ORIGINS = ['*'] + + # 测试环境API配置(使用测试密钥) + LLM_API_KEY = 'test-api-key' + WX_APPID = 'test-wx-appid' + WX_SECRET = 'test-wx-secret' + + # 测试环境性能配置 + SQLALCHEMY_ENGINE_OPTIONS = { + 'pool_pre_ping': False, # 测试环境关闭连接池检查 + 'pool_recycle': -1, + } + + @staticmethod + def init_app(app): + Config.init_app(app) + + # 测试环境特定初始化 + import logging + app.logger.setLevel(logging.DEBUG) + + # 测试环境控制台输出 + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.DEBUG) + console_handler.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s: %(message)s' + )) + app.logger.addHandler(console_handler) + + app.logger.info('测试环境启动') diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..c0d3c67 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,159 @@ +#!/bin/bash + +# Flask提示词大师部署脚本 +# 用于生产环境部署 + +set -e + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# 日志函数 +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 检查环境变量 +check_env() { + log_info "检查环境变量..." + + required_vars=( + "SECRET_KEY" + "DATABASE_URL" + "LLM_API_KEY" + "WX_APPID" + "WX_SECRET" + ) + + for var in "${required_vars[@]}"; do + if [ -z "${!var}" ]; then + log_error "环境变量 $var 未设置" + exit 1 + fi + done + + log_info "环境变量检查通过" +} + +# 安装依赖 +install_dependencies() { + log_info "安装Python依赖..." + pip install -r requirements.txt + log_info "依赖安装完成" +} + +# 数据库迁移 +run_migrations() { + log_info "运行数据库迁移..." + export FLASK_ENV=production + flask db upgrade + log_info "数据库迁移完成" +} + +# 构建Docker镜像 +build_docker() { + log_info "构建Docker镜像..." + docker build -t flask-prompt-master . + log_info "Docker镜像构建完成" +} + +# 启动Docker服务 +start_docker() { + log_info "启动Docker服务..." + docker-compose up -d + log_info "Docker服务启动完成" +} + +# 健康检查 +health_check() { + log_info "执行健康检查..." + + # 等待服务启动 + sleep 10 + + # 检查应用健康状态 + if curl -f http://localhost/health > /dev/null 2>&1; then + log_info "应用健康检查通过" + else + log_error "应用健康检查失败" + exit 1 + fi +} + +# 显示服务状态 +show_status() { + log_info "显示服务状态..." + docker-compose ps +} + +# 主函数 +main() { + log_info "开始部署Flask提示词大师..." + + # 检查环境变量 + check_env + + # 安装依赖 + install_dependencies + + # 运行数据库迁移 + run_migrations + + # 构建Docker镜像 + build_docker + + # 启动Docker服务 + start_docker + + # 健康检查 + health_check + + # 显示服务状态 + show_status + + log_info "部署完成!" + log_info "应用地址: http://localhost" + log_info "API地址: http://localhost/api" +} + +# 脚本入口 +case "${1:-deploy}" in + "deploy") + main + ;; + "build") + build_docker + ;; + "start") + start_docker + ;; + "stop") + log_info "停止Docker服务..." + docker-compose down + ;; + "restart") + log_info "重启Docker服务..." + docker-compose restart + ;; + "logs") + docker-compose logs -f + ;; + "status") + show_status + ;; + *) + echo "用法: $0 {deploy|build|start|stop|restart|logs|status}" + exit 1 + ;; +esac diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..80d6768 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,81 @@ +version: '3.8' + +services: + # Flask应用服务 + app: + build: . + container_name: flask_prompt_master + ports: + - "5000:5000" + environment: + - FLASK_ENV=production + - DATABASE_URL=mysql+pymysql://root:password@db:3306/prompt_master?charset=utf8mb4 + - REDIS_URL=redis://redis:6379/0 + env_file: + - .env + volumes: + - ./logs:/app/logs + - ./uploads:/app/uploads + depends_on: + - db + - redis + restart: unless-stopped + networks: + - app-network + + # MySQL数据库服务 + db: + image: mysql:8.0 + container_name: prompt_master_db + environment: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: prompt_master + MYSQL_USER: prompt_user + MYSQL_PASSWORD: prompt_password + volumes: + - mysql_data:/var/lib/mysql + - ./docker/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "3306:3306" + restart: unless-stopped + networks: + - app-network + command: --default-authentication-plugin=mysql_native_password + + # Redis缓存服务 + redis: + image: redis:7-alpine + container_name: prompt_master_redis + ports: + - "6379:6379" + volumes: + - redis_data:/data + restart: unless-stopped + networks: + - app-network + command: redis-server --appendonly yes + + # Nginx反向代理 + nginx: + image: nginx:alpine + container_name: prompt_master_nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf + - ./docker/nginx/ssl:/etc/nginx/ssl + - ./logs:/var/log/nginx + depends_on: + - app + restart: unless-stopped + networks: + - app-network + +volumes: + mysql_data: + redis_data: + +networks: + app-network: + driver: bridge diff --git a/docker/mysql/init.sql b/docker/mysql/init.sql new file mode 100644 index 0000000..3c2af23 --- /dev/null +++ b/docker/mysql/init.sql @@ -0,0 +1,26 @@ +-- MySQL数据库初始化脚本 +-- 用于Docker容器启动时自动创建数据库和用户 + +-- 创建数据库(如果不存在) +CREATE DATABASE IF NOT EXISTS prompt_master CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- 创建用户(如果不存在) +CREATE USER IF NOT EXISTS 'prompt_user'@'%' IDENTIFIED BY 'prompt_password'; + +-- 授予权限 +GRANT ALL PRIVILEGES ON prompt_master.* TO 'prompt_user'@'%'; + +-- 刷新权限 +FLUSH PRIVILEGES; + +-- 使用数据库 +USE prompt_master; + +-- 创建基础表结构(如果需要) +-- 注意:实际的表结构会由Flask-SQLAlchemy自动创建 +-- 这里只是示例,实际项目中可能不需要 + +-- 设置字符集 +SET NAMES utf8mb4; +SET CHARACTER SET utf8mb4; +SET character_set_connection=utf8mb4; diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 0000000..4bce3ce --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,137 @@ +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # 日志格式 + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + # 基本设置 + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + client_max_body_size 10M; + + # Gzip压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # 上游服务器配置 + upstream flask_app { + server app:5000; + keepalive 32; + } + + # HTTP服务器配置 + server { + listen 80; + server_name localhost; + + # 重定向到HTTPS + return 301 https://$server_name$request_uri; + } + + # HTTPS服务器配置 + server { + listen 443 ssl http2; + server_name localhost; + + # SSL配置 + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # 安全头 + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # 静态文件缓存 + location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + proxy_pass http://flask_app; + } + + # API路由 + location /api/ { + proxy_pass http://flask_app; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + } + + # 微信小程序接口 + location /wx/ { + proxy_pass http://flask_app; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # 健康检查 + location /health { + proxy_pass http://flask_app; + access_log off; + } + + # 默认路由 + location / { + proxy_pass http://flask_app; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_connect_timeout 30s; + proxy_send_timeout 30s; + proxy_read_timeout 30s; + } + + # 错误页面 + error_page 404 /404.html; + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + } +} diff --git a/docs/development/Windows生产环境部署指南.md b/docs/development/Windows生产环境部署指南.md new file mode 100644 index 0000000..37ab8ff --- /dev/null +++ b/docs/development/Windows生产环境部署指南.md @@ -0,0 +1,205 @@ +# Windows 生产环境部署指南 + +## 📋 概述 + +本指南介绍如何在 Windows 环境下部署 Flask 提示词大师应用的生产环境。 + +## 🎯 部署方案 + +### 方案一:Waitress 服务器(推荐) + +Waitress 是一个纯 Python 的 WSGI 服务器,完全兼容 Windows 环境。 + +#### 1. 安装依赖 + +```bash +pip install -r requirements.txt +``` + +#### 2. 启动生产环境 + +**方法一:使用 Python 脚本** +```bash +python run_production.py +``` + +**方法二:使用批处理文件** +```bash +start_production.bat +``` + +#### 3. 停止服务器 + +**方法一:Ctrl+C** +在运行窗口按 `Ctrl+C` 停止服务器 + +**方法二:使用批处理文件** +```bash +stop_production.bat +``` + +## ⚙️ 配置说明 + +### 环境变量配置 + +生产环境需要设置以下环境变量: + +```bash +# 设置生产环境 +set FLASK_ENV=production + +# 数据库配置 +set DATABASE_URL=mysql+pymysql://root:123456@localhost:3306/pro_db?charset=utf8mb4 + +# API 配置 +set LLM_API_URL=https://api.deepseek.com/v1 +set LLM_API_KEY=your-actual-api-key + +# 微信小程序配置 +set WX_APPID=your-wx-appid +set WX_SECRET=your-wx-secret + +# 安全配置 +set SECRET_KEY=your-secret-key +``` + +### 服务器配置 + +Waitress 服务器配置(在 `run_production.py` 中): + +```python +serve( + app, + host='0.0.0.0', # 监听所有网络接口 + port=5000, # 端口号 + threads=4, # 线程数 + connection_limit=1000, # 连接限制 + cleanup_interval=30, # 清理间隔 + channel_timeout=120, # 通道超时 + max_request_body_size=1073741824, # 最大请求体大小(1GB) +) +``` + +## 📊 性能优化 + +### 1. 数据库优化 + +- 使用连接池 +- 配置适当的索引 +- 定期清理日志表 + +### 2. 缓存策略 + +- 启用 Redis 缓存 +- 配置模板缓存 +- 实现 API 响应缓存 + +### 3. 日志管理 + +- 配置日志轮转 +- 设置日志级别 +- 监控错误日志 + +## 🔒 安全配置 + +### 1. 网络安全 + +- 配置防火墙规则 +- 使用 HTTPS +- 限制访问 IP + +### 2. 应用安全 + +- 启用 CSRF 保护 +- 配置 CORS 策略 +- 输入验证和过滤 + +### 3. 数据安全 + +- 数据库访问控制 +- 敏感信息加密 +- 定期备份 + +## 📈 监控和维护 + +### 1. 健康检查 + +访问健康检查接口: +``` +GET http://localhost:5000/health +``` + +### 2. 日志监控 + +- 应用日志:`logs/app.log` +- 访问日志:`logs/access.log` +- 错误日志:`logs/error.log` + +### 3. 性能监控 + +- CPU 使用率 +- 内存使用情况 +- 响应时间 +- 并发连接数 + +## 🚀 部署脚本 + +### 启动脚本 + +`start_production.bat` - 生产环境启动脚本 + +### 停止脚本 + +`stop_production.bat` - 生产环境停止脚本 + +### 部署脚本 + +`deploy.sh` - 自动化部署脚本(Linux 环境) + +## 🔧 故障排除 + +### 常见问题 + +1. **端口被占用** + ```bash + netstat -ano | findstr :5000 + taskkill /PID <进程ID> /F + ``` + +2. **数据库连接失败** + - 检查数据库服务状态 + - 验证连接字符串 + - 确认网络连接 + +3. **API 调用失败** + - 检查 API 密钥配置 + - 验证网络连接 + - 查看错误日志 + +### 日志分析 + +```bash +# 查看应用日志 +type logs\app.log + +# 查看错误日志 +type logs\error.log + +# 实时监控日志 +Get-Content logs\app.log -Wait +``` + +## 📞 技术支持 + +如遇到问题,请: + +1. 查看日志文件 +2. 检查配置参数 +3. 验证环境变量 +4. 联系技术支持 + +--- + +**版本**: 1.0 +**更新日期**: 2025-08-17 +**维护者**: 开发团队 diff --git a/docs/development/部署配置优化总结.md b/docs/development/部署配置优化总结.md new file mode 100644 index 0000000..de661ec --- /dev/null +++ b/docs/development/部署配置优化总结.md @@ -0,0 +1,408 @@ +# 部署配置优化总结 + +## 项目概述 + +本次优化成功解决了Flask提示词大师项目的**部署配置分散**问题,建立了统一、标准化、可扩展的部署配置管理系统。 + +## 优化成果 + +### ✅ 解决的问题 + +#### 1. **部署配置文件分散** +- **问题**:部署配置分散在多个位置 + - `uwsgi.ini` - 硬编码路径,配置不完整 + - `run_dev.py` - 开发环境配置 + - 缺乏生产环境配置 + - 缺乏容器化配置 + +- **解决方案**:统一部署配置结构 + ``` + 项目根目录/ + ├── uwsgi.ini # uWSGI配置(优化) + ├── gunicorn.conf.py # Gunicorn配置(新增) + ├── Dockerfile # Docker配置(新增) + ├── docker-compose.yml # Docker Compose配置(新增) + ├── deploy.sh # 部署脚本(新增) + └── docker/ # Docker相关配置 + ├── nginx/ + │ └── nginx.conf # Nginx配置 + └── mysql/ + └── init.sql # MySQL初始化脚本 + ``` + +#### 2. **部署环境不标准化** +- **问题**:缺乏标准化的部署环境配置 + - 开发、测试、生产环境配置混乱 + - 缺乏环境隔离 + - 缺乏健康检查机制 + +- **解决方案**:实现标准化部署环境 + - **开发环境**:Flask内置服务器 + - **测试环境**:Gunicorn + 测试配置 + - **生产环境**:Docker + Nginx + Gunicorn + +### ✅ 实现的功能 + +#### 1. **多服务器配置** +```python +# uWSGI配置(传统部署) +[uwsgi] +chdir = %(here) +wsgi-file = run_dev.py +callable = app +processes = 4 +threads = 2 + +# Gunicorn配置(现代部署) +bind = "0.0.0.0:5000" +workers = multiprocessing.cpu_count() * 2 + 1 +worker_class = "sync" +``` + +#### 2. **容器化部署** +```yaml +# Docker Compose配置 +services: + app: + build: . + ports: + - "5000:5000" + environment: + - FLASK_ENV=production + db: + image: mysql:8.0 + redis: + image: redis:7-alpine + nginx: + image: nginx:alpine +``` + +#### 3. **反向代理配置** +```nginx +# Nginx配置 +upstream flask_app { + server app:5000; + keepalive 32; +} + +server { + listen 443 ssl http2; + location / { + proxy_pass http://flask_app; + } +} +``` + +#### 4. **健康检查机制** +```python +@main_bp.route('/health') +def health_check(): + """健康检查接口""" + return jsonify({ + 'status': 'healthy', + 'timestamp': datetime.now().isoformat(), + 'environment': os.environ.get('FLASK_ENV', 'unknown') + }) +``` + +## 技术架构 + +### 部署架构图 +``` +┌─────────────────────────────────────────────────────────────┐ +│ 用户访问层 │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ +│ │ 浏览器 │ │ 移动端 │ │ 小程序 │ │ API │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Nginx反向代理层 │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ +│ │ SSL终止 │ │ 负载均衡 │ │ 静态文件 │ │ 缓存 │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 应用服务层 │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ +│ │ Gunicorn │ │ uWSGI │ │ Flask App │ │ 健康检查│ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 数据服务层 │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ +│ │ MySQL │ │ Redis │ │ 文件存储 │ │ 日志 │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 部署方式对比 + +#### 传统部署方式 +```bash +# 使用uWSGI +uwsgi --ini uwsgi.ini + +# 使用Gunicorn +gunicorn --config gunicorn.conf.py run_dev:app +``` + +#### 容器化部署方式 +```bash +# 使用Docker Compose +docker-compose up -d + +# 使用部署脚本 +./deploy.sh deploy +``` + +## 文件结构对比 + +### 优化前 +``` +项目根目录/ +├── uwsgi.ini # 基础uWSGI配置 +├── run_dev.py # 开发环境启动 +└── start.sh # 基础启动脚本 +``` + +### 优化后 +``` +项目根目录/ +├── uwsgi.ini # 优化的uWSGI配置 +├── gunicorn.conf.py # Gunicorn配置 +├── Dockerfile # Docker镜像配置 +├── docker-compose.yml # Docker Compose配置 +├── deploy.sh # 自动化部署脚本 +├── requirements.txt # 更新依赖 +└── docker/ # Docker相关配置 + ├── nginx/ + │ └── nginx.conf # Nginx反向代理配置 + └── mysql/ + └── init.sql # MySQL初始化脚本 +``` + +## 配置详情 + +### 1. uWSGI配置优化 +```ini +[uwsgi] +# 项目根目录 +chdir = %(here) + +# 环境变量设置 +env = FLASK_ENV=production + +# 内存限制 +memory-report = true +max-requests = 1000 +max-requests-delta = 100 + +# 优雅重启 +reload-on-rss = 2048 +reload-on-as = 512 +``` + +### 2. Gunicorn配置 +```python +# 服务器配置 +bind = "0.0.0.0:5000" +workers = multiprocessing.cpu_count() * 2 + 1 +worker_class = "sync" + +# 超时配置 +timeout = 30 +keepalive = 2 +graceful_timeout = 30 + +# 日志配置 +accesslog = "logs/gunicorn_access.log" +errorlog = "logs/gunicorn_error.log" +``` + +### 3. Docker配置 +```dockerfile +# 使用Python 3.9官方镜像 +FROM python:3.9-slim + +# 设置环境变量 +ENV FLASK_ENV=production +ENV FLASK_APP=run_dev.py + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:5000/health || exit 1 +``` + +### 4. Nginx配置 +```nginx +# 上游服务器配置 +upstream flask_app { + server app:5000; + keepalive 32; +} + +# SSL配置 +ssl_certificate /etc/nginx/ssl/cert.pem; +ssl_certificate_key /etc/nginx/ssl/key.pem; + +# 安全头 +add_header X-Frame-Options DENY; +add_header X-Content-Type-Options nosniff; +add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; +``` + +## 部署流程 + +### 1. 环境准备 +```bash +# 安装Docker和Docker Compose +sudo apt-get update +sudo apt-get install docker.io docker-compose + +# 设置环境变量 +cp env.example .env +# 编辑.env文件设置实际值 +``` + +### 2. 部署执行 +```bash +# 使用部署脚本 +chmod +x deploy.sh +./deploy.sh deploy + +# 或手动部署 +docker-compose up -d +``` + +### 3. 健康检查 +```bash +# 检查服务状态 +./deploy.sh status + +# 查看日志 +./deploy.sh logs + +# 健康检查 +curl http://localhost/health +``` + +## 监控和维护 + +### 1. 日志管理 +```bash +# 查看应用日志 +docker-compose logs app + +# 查看Nginx日志 +docker-compose logs nginx + +# 查看数据库日志 +docker-compose logs db +``` + +### 2. 性能监控 +```bash +# 查看容器资源使用 +docker stats + +# 查看服务状态 +docker-compose ps + +# 查看健康状态 +curl http://localhost/health +``` + +### 3. 备份和恢复 +```bash +# 数据库备份 +docker-compose exec db mysqldump -u root -p prompt_master > backup.sql + +# 数据库恢复 +docker-compose exec -T db mysql -u root -p prompt_master < backup.sql +``` + +## 最佳实践 + +### 1. 环境分离 +- 开发环境:使用Flask内置服务器 +- 测试环境:使用Gunicorn + 测试配置 +- 生产环境:使用Docker + Nginx + Gunicorn + +### 2. 安全配置 +- 使用HTTPS和SSL证书 +- 配置安全头 +- 限制文件上传大小 +- 使用环境变量管理敏感信息 + +### 3. 性能优化 +- 启用Gzip压缩 +- 配置静态文件缓存 +- 使用连接池 +- 配置负载均衡 + +### 4. 监控告警 +- 健康检查机制 +- 日志监控 +- 性能指标监控 +- 错误告警 + +## 后续优化建议 + +### 短期优化 +1. **监控系统集成** + - 集成Prometheus监控 + - 配置Grafana仪表板 + - 设置告警规则 + +2. **CI/CD流水线** + - 配置GitHub Actions + - 自动化测试和部署 + - 蓝绿部署策略 + +### 长期规划 +1. **微服务架构** + - 服务拆分 + - API网关 + - 服务发现 + +2. **云原生部署** + - Kubernetes部署 + - 服务网格 + - 自动扩缩容 + +## 总结 + +本次部署配置优化成功实现了以下目标: + +### ✅ 解决的问题 +- **部署配置文件统一**:所有部署配置集中在项目根目录 +- **部署环境标准化**:支持多种部署方式 +- **容器化支持**:完整的Docker部署方案 +- **监控机制完善**:健康检查和日志管理 + +### ✅ 实现的功能 +- **多服务器支持**:uWSGI和Gunicorn配置 +- **容器化部署**:Docker + Docker Compose +- **反向代理**:Nginx配置和SSL支持 +- **健康检查**:应用健康状态监控 +- **自动化部署**:一键部署脚本 + +### ✅ 部署方式 +- **传统部署**:uWSGI/Gunicorn + Nginx +- **容器化部署**:Docker Compose +- **云原生部署**:为Kubernetes预留接口 + +新的部署配置系统完全符合软件开发5S规范中的"整顿"和"清洁"原则,为项目的生产部署提供了标准化、可扩展的解决方案。现在您可以轻松地在不同环境中部署应用,并且具备完善的监控和维护能力。 + +--- + +**优化完成时间**:2025年8月17日 +**优化版本**:v1.0 +**测试状态**:✅ 配置完成 +**文档状态**:✅ 完整 diff --git a/docs/development/配置系统重构总结.md b/docs/development/配置系统重构总结.md new file mode 100644 index 0000000..0112e5c --- /dev/null +++ b/docs/development/配置系统重构总结.md @@ -0,0 +1,306 @@ +# 配置系统重构总结 + +## 项目概述 + +本次重构成功解决了Flask提示词大师项目中的**配置文件分散**问题,建立了统一、安全、可维护的分层配置管理系统。 + +## 重构成果 + +### ✅ 解决的问题 + +#### 1. **配置文件位置不统一** +- **问题**:配置文件分散在多个位置 + - `src/flask_prompt_master/config.py` + - `uwsgi.ini` + - `tests/test_api.py` 中的 `TestConfig` + - `run_dev.py` 中的硬编码配置 + +- **解决方案**:统一配置目录结构 + ``` + config/ + ├── __init__.py # 配置工厂和映射 + ├── base.py # 基础配置类 + ├── development.py # 开发环境配置 + ├── production.py # 生产环境配置 + ├── testing.py # 测试环境配置 + └── local.py # 本地配置(git忽略) + ``` + +#### 2. **环境配置管理不清晰** +- **问题**:所有环境使用同一套配置,缺乏环境分离 + +- **解决方案**:实现环境分离配置 + - **开发环境**:调试模式,SQLite数据库,关闭CSRF + - **生产环境**:关闭调试,Redis缓存,邮件错误报告 + - **测试环境**:内存数据库,测试密钥,最短缓存时间 + - **本地环境**:可自定义,不被版本控制 + +### ✅ 实现的功能 + +#### 1. **配置工厂模式** +```python +from config import get_config + +# 根据环境变量自动选择配置 +config_class = get_config() +app.config.from_object(config_class) +``` + +#### 2. **环境变量标准化** +``` +env.example # 环境变量示例(137行详细说明) +env.test # 测试环境变量 +env.production # 生产环境变量 +.env # 本地环境变量(git忽略) +``` + +#### 3. **配置验证机制** +- 必需环境变量验证 +- 配置项完整性检查 +- 环境特定配置验证 + +#### 4. **安全性提升** +- 移除所有硬编码的敏感信息 +- 使用环境变量管理敏感配置 +- 确保 `.env` 文件不被版本控制 + +## 技术架构 + +### 配置系统架构图 +``` +┌─────────────────────────────────────────────────────────────┐ +│ 配置工厂 (config/__init__.py) │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ +│ │ Development │ │ Production │ │ Testing │ │ Local │ │ +│ │ Config │ │ Config │ │ Config │ │ Config │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 基础配置 (config/base.py) │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ +│ │ Flask配置 │ │ 数据库配置 │ │ API配置 │ │ 安全配置 │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 环境变量管理 │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐ │ +│ │ .env │ │ env.test │ │env.production│ │env.example│ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 环境切换机制 +```python +# 通过环境变量切换配置 +export FLASK_ENV=development # 开发环境 +export FLASK_ENV=production # 生产环境 +export FLASK_ENV=testing # 测试环境 +export FLASK_ENV=local # 本地环境 +``` + +## 文件结构对比 + +### 重构前 +``` +项目根目录/ +├── src/flask_prompt_master/ +│ └── config.py # 单一配置文件 +├── uwsgi.ini # 部署配置 +├── tests/test_api.py # 测试配置 +└── run_dev.py # 硬编码配置 +``` + +### 重构后 +``` +项目根目录/ +├── config/ # 统一配置目录 +│ ├── __init__.py # 配置工厂 +│ ├── base.py # 基础配置 +│ ├── development.py # 开发环境 +│ ├── production.py # 生产环境 +│ ├── testing.py # 测试环境 +│ ├── local.py # 本地配置 +│ └── README.md # 配置说明 +├── env.example # 环境变量示例 +├── env.test # 测试环境变量 +├── env.production # 生产环境变量 +├── .env # 本地环境变量 +└── docs/development/ + ├── 配置迁移指南.md # 迁移指南 + └── 配置系统重构总结.md # 本文档 +``` + +## 测试验证 + +### 测试结果 +``` +============================================================ +🔧 Flask提示词大师 - 简化配置系统测试 +============================================================ +🔧 测试1: 配置加载功能 + ✅ 配置加载成功: type + +🔄 测试2: 环境切换功能 + ✅ development 环境: type + 调试模式: True + 测试模式: False + ✅ testing 环境: type + 调试模式: False + 测试模式: True + ✅ production 环境: type + 调试模式: False + 测试模式: False + +🚀 测试3: 应用创建功能 + ✅ 应用创建成功 + 调试模式: True + 数据库: + +📋 测试4: 配置属性验证 + ✅ SECRET_KEY: *************************** + ✅ SQLALCHEMY_DATABASE_URI: + ✅ LLM_API_URL: https://api.test.com/v1 + ✅ LLM_API_KEY: ************ + ✅ WX_APPID: test-wx-appid + ✅ WX_SECRET: ************** + ✅ DEBUG: True + ✅ TESTING: False + +============================================================ +🎉 所有核心测试通过!配置系统工作正常。 +============================================================ +``` + +## 使用指南 + +### 1. 设置环境变量 +```bash +# 复制环境变量示例文件 +cp env.example .env + +# 编辑 .env 文件,设置实际的环境变量值 +SECRET_KEY=your-actual-secret-key +DATABASE_URL=your-actual-database-url +LLM_API_KEY=your-actual-api-key +WX_APPID=your-actual-wx-appid +WX_SECRET=your-actual-wx-secret +``` + +### 2. 切换环境 +```bash +# 开发环境 +export FLASK_ENV=development +python run_dev.py + +# 测试环境 +export FLASK_ENV=testing +python -m pytest tests/ + +# 生产环境 +export FLASK_ENV=production +python run_dev.py +``` + +### 3. 在代码中使用配置 +```python +# 应用初始化 +from config import get_config + +def create_app(config_class=None): + if config_class is None: + config_class = get_config() + app = Flask(__name__) + app.config.from_object(config_class) + return app + +# 在路由中使用配置 +from flask import current_app + +def get_openai_client(): + return OpenAI( + api_key=current_app.config['LLM_API_KEY'], + base_url=current_app.config['LLM_API_URL'] + ) +``` + +## 最佳实践 + +### 1. 环境分离 +- 不同环境使用不同的配置 +- 避免在代码中硬编码环境特定配置 +- 使用环境变量管理敏感信息 + +### 2. 配置验证 +- 应用启动时验证必需配置项 +- 使用类型检查和默认值 +- 提供清晰的错误信息 + +### 3. 安全性 +- 敏感信息通过环境变量管理 +- 确保 `.env` 文件不被版本控制 +- 使用强密码和密钥 + +### 4. 可维护性 +- 配置结构清晰,易于理解 +- 提供详细的配置文档 +- 支持配置热重载 + +## 后续优化建议 + +### 短期优化 +1. **配置验证增强** + - 添加配置项类型验证 + - 实现配置依赖检查 + - 添加配置项范围验证 + +2. **监控和告警** + - 配置变更监控 + - 敏感配置泄露检测 + - 配置健康检查 + +### 长期规划 +1. **配置管理工具** + - 集成 `python-decouple` 等工具 + - 实现配置热重载 + - 添加配置版本管理 + +2. **部署自动化** + - 配置自动生成 + - 环境自动检测 + - 配置备份和恢复 + +## 总结 + +本次配置系统重构成功实现了以下目标: + +### ✅ 解决的问题 +- **配置文件位置统一**:所有配置集中在 `config/` 目录 +- **环境配置管理清晰**:支持4种环境,配置分离明确 +- **安全性大幅提升**:敏感信息不再硬编码 +- **可维护性增强**:配置结构清晰,易于管理 +- **部署简化**:标准化的环境切换机制 + +### ✅ 实现的功能 +- **配置工厂模式**:自动环境切换 +- **环境变量标准化**:统一的环境变量管理 +- **配置验证机制**:确保配置完整性 +- **安全性提升**:敏感信息保护 + +### ✅ 测试验证 +- **配置加载**:✅ 正常 +- **环境切换**:✅ 正常 +- **应用创建**:✅ 正常 +- **配置属性**:✅ 正常 + +新的配置系统完全符合软件开发5S规范中的"整理"和"清洁"原则,为项目的后续开发和部署奠定了坚实的基础。现在您可以安全地管理不同环境的配置,并且敏感信息得到了妥善保护。 + +--- + +**重构完成时间**:2025年8月17日 +**重构版本**:v1.0 +**测试状态**:✅ 全部通过 +**文档状态**:✅ 完整 diff --git a/docs/development/配置迁移指南.md b/docs/development/配置迁移指南.md new file mode 100644 index 0000000..cbb91d2 --- /dev/null +++ b/docs/development/配置迁移指南.md @@ -0,0 +1,223 @@ +# 配置系统迁移指南 + +## 概述 + +本文档指导您从旧的配置系统迁移到新的分层配置管理系统。 + +## 迁移前准备 + +### 1. 备份当前配置 +```bash +# 备份当前的配置文件 +cp src/flask_prompt_master/config.py src/flask_prompt_master/config.py.backup +``` + +### 2. 安装新依赖 +```bash +pip install python-dotenv==1.0.0 +``` + +## 迁移步骤 + +### 第一步:环境变量设置 + +1. **复制环境变量示例文件** + ```bash + cp env.example .env + ``` + +2. **编辑 .env 文件** + 根据您的实际环境设置以下必需的环境变量: + ```bash + # Flask基础配置 + SECRET_KEY=your-actual-secret-key + FLASK_ENV=development + + # 数据库配置 + DATABASE_URL=mysql+pymysql://username:password@localhost:3306/database_name?charset=utf8mb4 + + # OpenAI兼容API配置 + LLM_API_URL=https://api.deepseek.com/v1 + LLM_API_KEY=your-actual-api-key + + # 微信小程序配置 + WX_APPID=your-actual-wx-appid + WX_SECRET=your-actual-wx-secret + ``` + +### 第二步:验证配置 + +1. **测试配置系统** + ```bash + # 设置环境变量 + export FLASK_ENV=development + + # 测试配置加载 + python -c "from config import get_config; print('配置系统正常')" + ``` + +2. **测试应用启动** + ```bash + python -c "from src.flask_prompt_master import create_app; app = create_app(); print('应用创建成功')" + ``` + +### 第三步:更新代码引用 + +#### 1. 应用初始化文件 +旧代码: +```python +from src.flask_prompt_master.config import Config + +def create_app(config_class=Config): + app = Flask(__name__) + app.config.from_object(config_class) +``` + +新代码: +```python +from config import get_config + +def create_app(config_class=None): + if config_class is None: + config_class = get_config() + app = Flask(__name__) + app.config.from_object(config_class) + config_class.init_app(app) +``` + +#### 2. 路由文件中的配置引用 +旧代码: +```python +from src.flask_prompt_master.config import Config + +client = OpenAI(api_key=Config.LLM_API_KEY, base_url=Config.LLM_API_URL) +``` + +新代码: +```python +def get_openai_client(): + return OpenAI( + api_key=current_app.config['LLM_API_KEY'], + base_url=current_app.config['LLM_API_URL'] + ) + +client = get_openai_client() +``` + +### 第四步:环境切换测试 + +1. **开发环境** + ```bash + export FLASK_ENV=development + python run_dev.py + ``` + +2. **测试环境** + ```bash + export FLASK_ENV=testing + python -m pytest tests/ + ``` + +3. **生产环境** + ```bash + export FLASK_ENV=production + # 确保设置了所有必需的环境变量 + python run_dev.py + ``` + +## 配置项对比 + +### 旧配置系统 +```python +class Config: + SECRET_KEY = 'dev-key' + SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:123456@localhost:3306/pro_db?charset=utf8mb4' + LLM_API_KEY = 'sk-fdf7cc1c73504e628ec0119b7e11b8cc' + WX_APPID = 'wx2c65877d37fc29bf' + WX_SECRET = '89aa97dda3c1347c6ae3d6ab4627f1f4' +``` + +### 新配置系统 +```python +# 环境变量设置 +SECRET_KEY=your-secret-key +DATABASE_URL=mysql+pymysql://username:password@localhost:3306/database_name?charset=utf8mb4 +LLM_API_KEY=your-api-key +WX_APPID=your-wx-appid +WX_SECRET=your-wx-secret +``` + +## 常见问题解决 + +### 1. 环境变量未设置错误 +**错误信息:** `ValueError: SECRET_KEY 环境变量未设置` + +**解决方案:** +- 确保 `.env` 文件存在且包含所有必需的环境变量 +- 检查环境变量名称是否正确 +- 确保 `python-dotenv` 已安装 + +### 2. 配置类导入错误 +**错误信息:** `ModuleNotFoundError: No module named 'config'` + +**解决方案:** +- 确保在项目根目录下运行命令 +- 检查 `config/` 目录是否存在 +- 确保 `config/__init__.py` 文件存在 + +### 3. 跨域配置错误 +**错误信息:** `ValueError: 生产环境必须设置 CORS_ORIGINS 环境变量` + +**解决方案:** +- 在生产环境中设置 `CORS_ORIGINS` 环境变量 +- 或者修改生产环境配置使用默认值 + +## 迁移检查清单 + +- [ ] 备份旧配置文件 +- [ ] 安装新依赖包 +- [ ] 创建 `.env` 文件并设置环境变量 +- [ ] 更新应用初始化代码 +- [ ] 更新路由文件中的配置引用 +- [ ] 测试开发环境配置 +- [ ] 测试测试环境配置 +- [ ] 测试生产环境配置 +- [ ] 验证所有功能正常工作 +- [ ] 删除旧配置文件 + +## 回滚方案 + +如果迁移过程中出现问题,可以快速回滚: + +1. **恢复旧配置** + ```bash + cp src/flask_prompt_master/config.py.backup src/flask_prompt_master/config.py + ``` + +2. **恢复旧的应用初始化代码** + ```python + from src.flask_prompt_master.config import Config + + def create_app(config_class=Config): + app = Flask(__name__) + app.config.from_object(config_class) + return app + ``` + +3. **恢复旧的路由配置引用** + ```python + from src.flask_prompt_master.config import Config + + client = OpenAI(api_key=Config.LLM_API_KEY, base_url=Config.LLM_API_URL) + ``` + +## 总结 + +新的配置系统提供了以下优势: + +1. **环境分离**:不同环境使用不同的配置 +2. **安全性提升**:敏感信息通过环境变量管理 +3. **可维护性增强**:配置结构清晰,易于管理 +4. **部署简化**:标准化的环境切换机制 + +完成迁移后,您的应用将具有更好的配置管理能力和安全性。 diff --git a/env.example b/env.example new file mode 100644 index 0000000..71000b5 --- /dev/null +++ b/env.example @@ -0,0 +1,136 @@ +# ======================================== +# Flask提示词大师应用环境变量配置示例 +# ======================================== +# 复制此文件为 .env 并根据实际情况修改配置 +# cp env.example .env + +# ======================================== +# Flask基础配置 +# ======================================== +# Flask应用密钥(必需) +SECRET_KEY=your-secret-key-here + +# 应用环境(development/production/testing/local) +FLASK_ENV=development + +# ======================================== +# 数据库配置 +# ======================================== +# 数据库连接URL(必需) +# MySQL示例: mysql+pymysql://username:password@localhost:3306/database_name?charset=utf8mb4 +# SQLite示例: sqlite:///app.db +# PostgreSQL示例: postgresql://username:password@localhost:5432/database_name +DATABASE_URL=mysql+pymysql://root:123456@localhost:3306/pro_db?charset=utf8mb4 + +# ======================================== +# OpenAI兼容API配置 +# ======================================== +# API基础URL(必需) +LLM_API_URL=https://api.deepseek.com/v1 + +# API密钥(必需) +LLM_API_KEY=sk-your-api-key-here + +# ======================================== +# 微信小程序配置 +# ======================================== +# 小程序AppID(必需) +WX_APPID=wx-your-appid-here + +# 小程序Secret(必需) +WX_SECRET=your-wx-secret-here + +# ======================================== +# 跨域配置 +# ======================================== +# 允许跨域的域名,多个用逗号分隔 +# 开发环境: http://localhost:3000,http://127.0.0.1:3000 +# 生产环境: https://yourdomain.com,https://www.yourdomain.com +CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000 + +# ======================================== +# 日志配置 +# ======================================== +# 日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL) +LOG_LEVEL=INFO + +# 日志文件路径 +LOG_FILE=logs/app.log + +# ======================================== +# 缓存配置 +# ======================================== +# 缓存类型(simple/redis/memcached) +CACHE_TYPE=simple + +# 缓存默认超时时间(秒) +CACHE_DEFAULT_TIMEOUT=300 + +# Redis缓存URL(当CACHE_TYPE=redis时使用) +# REDIS_URL=redis://localhost:6379/0 + +# ======================================== +# 会话配置 +# ======================================== +# 会话生命周期(小时) +SESSION_LIFETIME_HOURS=24 + +# ======================================== +# 文件上传配置 +# ======================================== +# 最大文件上传大小(字节) +MAX_CONTENT_LENGTH=16777216 + +# 文件上传目录 +UPLOAD_FOLDER=uploads + +# ======================================== +# 安全配置 +# ======================================== +# 是否启用CSRF保护 +WTF_CSRF_ENABLED=True + +# CSRF令牌超时时间(秒) +WTF_CSRF_TIME_LIMIT=3600 + +# ======================================== +# 邮件配置(生产环境错误报告) +# ======================================== +# 邮件服务器地址 +# MAIL_SERVER=smtp.gmail.com + +# 邮件服务器端口 +# MAIL_PORT=587 + +# 发件人邮箱 +# MAIL_FROM=noreply@yourdomain.com + +# 管理员邮箱(多个用逗号分隔) +# ADMIN_EMAIL=admin@yourdomain.com + +# ======================================== +# 性能配置 +# ======================================== +# 数据库连接池大小 +# DB_POOL_SIZE=20 + +# 数据库连接池最大溢出连接数 +# DB_MAX_OVERFLOW=30 + +# ======================================== +# 开发工具配置 +# ======================================== +# 是否启用自动重载 +# FLASK_DEBUG=True + +# 是否启用详细错误页面 +# FLASK_DEBUG_TB_ENABLED=True + +# ======================================== +# 监控配置 +# ======================================== +# 是否启用性能监控 +# ENABLE_MONITORING=False + +# 监控数据收集间隔(秒) +# MONITORING_INTERVAL=60 diff --git a/env.production b/env.production new file mode 100644 index 0000000..71304a9 --- /dev/null +++ b/env.production @@ -0,0 +1,57 @@ +# ======================================== +# 生产环境变量配置 +# ======================================== +# 注意:生产环境必须设置所有必需的环境变量 +# 请根据实际部署环境修改这些配置 + +# Flask基础配置 +SECRET_KEY=your-production-secret-key-must-be-very-secure +FLASK_ENV=production + +# 数据库配置 +DATABASE_URL=mysql+pymysql://username:password@localhost:3306/production_db?charset=utf8mb4 + +# OpenAI兼容API配置 +LLM_API_URL=https://api.deepseek.com/v1 +LLM_API_KEY=sk-your-production-api-key + +# 微信小程序配置 +WX_APPID=wx-your-production-appid +WX_SECRET=your-production-wx-secret + +# 跨域配置(生产环境必须设置具体域名) +CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com + +# 日志配置 +LOG_LEVEL=WARNING +LOG_FILE=logs/production.log + +# 缓存配置 +CACHE_TYPE=redis +CACHE_DEFAULT_TIMEOUT=3600 +REDIS_URL=redis://localhost:6379/0 + +# 会话配置 +SESSION_LIFETIME_HOURS=168 + +# 文件上传配置 +MAX_CONTENT_LENGTH=8388608 +UPLOAD_FOLDER=uploads/production + +# 安全配置 +WTF_CSRF_ENABLED=True +WTF_CSRF_TIME_LIMIT=3600 + +# 邮件配置(错误报告) +MAIL_SERVER=smtp.gmail.com +MAIL_PORT=587 +MAIL_FROM=noreply@yourdomain.com +ADMIN_EMAIL=admin@yourdomain.com + +# 性能配置 +DB_POOL_SIZE=20 +DB_MAX_OVERFLOW=30 + +# 监控配置 +ENABLE_MONITORING=True +MONITORING_INTERVAL=60 diff --git a/env.test b/env.test new file mode 100644 index 0000000..d907e84 --- /dev/null +++ b/env.test @@ -0,0 +1,40 @@ +# ======================================== +# 测试环境变量配置 +# ======================================== + +# Flask基础配置 +SECRET_KEY=test-secret-key-for-testing-only +FLASK_ENV=testing + +# 数据库配置(使用内存数据库) +DATABASE_URL=sqlite:///:memory: + +# OpenAI兼容API配置(使用测试密钥) +LLM_API_URL=https://api.test.com/v1 +LLM_API_KEY=test-api-key + +# 微信小程序配置(使用测试密钥) +WX_APPID=test-wx-appid +WX_SECRET=test-wx-secret + +# 跨域配置 +CORS_ORIGINS=* + +# 日志配置 +LOG_LEVEL=DEBUG +LOG_FILE=logs/test.log + +# 缓存配置 +CACHE_TYPE=simple +CACHE_DEFAULT_TIMEOUT=10 + +# 会话配置 +SESSION_LIFETIME_HOURS=1 + +# 文件上传配置 +MAX_CONTENT_LENGTH=1048576 +UPLOAD_FOLDER=uploads/test + +# 安全配置 +WTF_CSRF_ENABLED=False +WTF_CSRF_TIME_LIMIT=300 diff --git a/gunicorn.conf.py b/gunicorn.conf.py new file mode 100644 index 0000000..f960fe3 --- /dev/null +++ b/gunicorn.conf.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +""" +Gunicorn配置文件 +用于生产环境部署 +""" + +import os +import multiprocessing + +# 服务器配置 +bind = "0.0.0.0:5000" +workers = multiprocessing.cpu_count() * 2 + 1 +worker_class = "sync" +worker_connections = 1000 +max_requests = 1000 +max_requests_jitter = 100 + +# 超时配置 +timeout = 30 +keepalive = 2 +graceful_timeout = 30 + +# 日志配置 +accesslog = "logs/gunicorn_access.log" +errorlog = "logs/gunicorn_error.log" +loglevel = "info" +access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s' + +# 进程配置 +preload_app = True +daemon = False +pidfile = "logs/gunicorn.pid" +user = None +group = None +tmp_upload_dir = None + +# 安全配置 +limit_request_line = 4094 +limit_request_fields = 100 +limit_request_field_size = 8190 + +# 环境变量 +raw_env = [ + "FLASK_ENV=production", +] + +# 钩子函数 +def on_starting(server): + """服务器启动时的钩子""" + server.log.info("Gunicorn服务器启动中...") + +def on_reload(server): + """重载时的钩子""" + server.log.info("Gunicorn服务器重载中...") + +def worker_int(worker): + """工作进程中断时的钩子""" + worker.log.info("工作进程 %s 被中断", worker.pid) + +def pre_fork(server, worker): + """fork工作进程前的钩子""" + server.log.info("工作进程 %s 即将启动", worker.pid) + +def post_fork(server, worker): + """fork工作进程后的钩子""" + server.log.info("工作进程 %s 已启动", worker.pid) + +def post_worker_init(worker): + """工作进程初始化后的钩子""" + worker.log.info("工作进程 %s 初始化完成", worker.pid) + +def worker_abort(worker): + """工作进程异常退出时的钩子""" + worker.log.info("工作进程 %s 异常退出", worker.pid) diff --git a/requirements.txt b/requirements.txt index 16a7779..b0100c4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,9 @@ -flask==2.0.1 -flask-cors==3.0.10 \ No newline at end of file +flask>=2.2.0 +flask-cors>=3.0.10 +python-dotenv>=1.0.0 +openai>=1.3.0 +flask-sqlalchemy>=3.0.2 +flask-migrate>=4.0.4 +pymysql>=1.1.0 +waitress>=3.0.0 +redis>=5.0.1 \ No newline at end of file diff --git a/run_production_simple.py b/run_production_simple.py new file mode 100644 index 0000000..cddd5cc --- /dev/null +++ b/run_production_simple.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +""" +简单的生产环境启动脚本 +使用 Flask 内置服务器,但配置为生产模式 +适合 Windows 环境快速部署 +""" + +import os +import sys +from src.flask_prompt_master import create_app + +def main(): + """主函数""" + # 设置生产环境变量 + os.environ['FLASK_ENV'] = 'production' + + # 创建应用实例 + app = create_app() + + print("=" * 60) + print("🚀 Flask 提示词大师 - 生产环境启动") + print("=" * 60) + print(f"📊 环境: {os.environ.get('FLASK_ENV', 'unknown')}") + print(f"🌐 服务器: Flask 内置服务器") + print(f"🔗 地址: http://0.0.0.0:5000") + print(f"📝 日志: 控制台输出") + print("=" * 60) + print("✅ 服务器启动中...") + print("💡 按 Ctrl+C 停止服务器") + print("=" * 60) + + try: + # 启动 Flask 内置服务器(生产模式配置) + app.run( + host='0.0.0.0', + port=5000, + debug=False, # 生产环境关闭调试 + threaded=True, # 启用多线程 + use_reloader=False # 关闭自动重载 + ) + except KeyboardInterrupt: + print("\n" + "=" * 60) + print("🛑 服务器已停止") + print("=" * 60) + except Exception as e: + print(f"\n❌ 启动失败: {str(e)}") + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/src/flask_prompt_master/__init__.py b/src/flask_prompt_master/__init__.py index f91bce8..21cf366 100644 --- a/src/flask_prompt_master/__init__.py +++ b/src/flask_prompt_master/__init__.py @@ -1,23 +1,40 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate -from src.flask_prompt_master.config import Config -import os from flask_cors import CORS +import os +from dotenv import load_dotenv + +# 加载环境变量 +load_dotenv() + +# 导入新的配置系统 +from config import get_config # 初始化扩展 db = SQLAlchemy() migrate = Migrate() -def create_app(config_class=Config): +def create_app(config_class=None): + """ + 应用工厂函数 + 使用新的配置系统自动选择环境配置 + """ app = Flask(__name__, template_folder='templates', static_folder='../static') + # 如果没有指定配置类,使用新的配置系统 + if config_class is None: + config_class = get_config() + app.config.from_object(config_class) + + # 初始化配置 + config_class.init_app(app) # 启用跨域支持 - CORS(app) + CORS(app, origins=app.config.get('CORS_ORIGINS', ['*'])) # 初始化扩展 db.init_app(app) @@ -27,4 +44,9 @@ def create_app(config_class=Config): from src.flask_prompt_master.routes import main_bp app.register_blueprint(main_bp) + # 记录应用启动信息 + app.logger.info(f"应用启动 - 环境: {os.environ.get('FLASK_ENV', 'development')}") + app.logger.info(f"调试模式: {app.config.get('DEBUG', False)}") + app.logger.info(f"数据库: {app.config.get('SQLALCHEMY_DATABASE_URI', 'Not set')}") + return app \ No newline at end of file diff --git a/src/flask_prompt_master/routes/routes.py b/src/flask_prompt_master/routes/routes.py index b3e5888..f60cb89 100644 --- a/src/flask_prompt_master/routes/routes.py +++ b/src/flask_prompt_master/routes/routes.py @@ -3,21 +3,39 @@ from openai import OpenAI from src.flask_prompt_master import db from src.flask_prompt_master.models import User, Prompt, Feedback, PromptTemplate, WxUser from src.flask_prompt_master.forms import PromptForm, FeedbackForm -from src.flask_prompt_master.config import Config import pymysql from datetime import datetime import requests import hashlib import time import json +import os main_bp = Blueprint('main', __name__) -client = OpenAI(api_key=Config.LLM_API_KEY, base_url=Config.LLM_API_URL) +@main_bp.route('/health') +def health_check(): + """健康检查接口""" + return jsonify({ + 'status': 'healthy', + 'timestamp': datetime.now().isoformat(), + 'environment': os.environ.get('FLASK_ENV', 'unknown') + }) -# 从配置中获取微信小程序配置 -WX_APPID = Config.WX_APPID -WX_SECRET = Config.WX_SECRET +# 使用current_app获取配置,而不是直接导入 +def get_openai_client(): + """获取OpenAI客户端实例""" + return OpenAI( + api_key=current_app.config['LLM_API_KEY'], + base_url=current_app.config['LLM_API_URL'] + ) + +def get_wx_config(): + """获取微信小程序配置""" + return { + 'appid': current_app.config['WX_APPID'], + 'secret': current_app.config['WX_SECRET'] + } def get_system_prompt(template_id=None): """获取系统提示词模板""" @@ -48,6 +66,9 @@ def generate_with_llm(input_text, template_id=None): try: system_prompt = get_system_prompt(template_id) + # 获取OpenAI客户端 + client = get_openai_client() + # 打印参数 print("\n=== API 调用参数 ===") print(f"模板ID: {template_id}") @@ -55,6 +76,35 @@ def generate_with_llm(input_text, template_id=None): print(f"系统提示: {system_prompt}") print("==================\n") + # 开发环境模拟API响应(当API密钥无效时) + api_key = current_app.config.get('LLM_API_KEY', '') + print(f"\n=== 当前API密钥: {api_key} ===") + + if api_key in ['test-api-key', 'your-actual-api-key-here', 'sk-your-api-key-here'] or 'test' in api_key.lower() or 'your-api-key' in api_key.lower(): + # 模拟API响应 + mock_responses = { + "写一篇关于python的文章": "请帮我写一篇关于Python编程语言的技术文章,要求:\n1. 介绍Python的基本特性和优势\n2. 包含实际代码示例\n3. 适合初学者阅读\n4. 字数在1000-1500字之间\n5. 结构清晰,逻辑性强", + "python代码优化": "请帮我优化以下Python代码,要求:\n1. 提高代码执行效率\n2. 改善代码可读性\n3. 遵循Python编码规范\n4. 添加必要的注释\n5. 考虑内存使用优化", + "python程序优化": "请帮我优化Python程序,要求:\n1. 分析程序性能瓶颈\n2. 提供优化建议\n3. 展示优化前后对比\n4. 考虑不同场景的优化策略\n5. 提供可执行的优化代码" + } + + # 根据输入文本返回相应的模拟响应 + for key, value in mock_responses.items(): + if key in input_text: + generated_text = value + print("\n=== 模拟API响应结果 ===") + print(f"生成的提示词: {generated_text}") + print("==================\n") + return generated_text + + # 默认模拟响应 + generated_text = f"请帮我{input_text},要求:\n1. 内容专业且实用\n2. 结构清晰,逻辑性强\n3. 包含具体示例\n4. 适合目标受众\n5. 符合行业标准" + print("\n=== 模拟API响应结果 ===") + print(f"生成的提示词: {generated_text}") + print("==================\n") + return generated_text + + # 真实API调用 response = client.chat.completions.create( model="deepseek-chat", messages=[ @@ -349,10 +399,13 @@ def wx_get_template_detail(template_id): def wx_login(): """微信小程序登录接口""" try: + # 获取微信小程序配置 + wx_config = get_wx_config() + # 添加调试日志 print("\n=== 微信登录配置 ===") - print(f"APPID: {WX_APPID}") - print(f"SECRET: {WX_SECRET}") + print(f"APPID: {wx_config['appid']}") + print(f"SECRET: {wx_config['secret']}") print("==================\n") data = request.get_json() @@ -369,8 +422,8 @@ def wx_login(): # 请求微信接口 wx_url = 'https://api.weixin.qq.com/sns/jscode2session' params = { - 'appid': WX_APPID, - 'secret': WX_SECRET, + 'appid': wx_config['appid'], + 'secret': wx_config['secret'], 'js_code': code, 'grant_type': 'authorization_code' } @@ -850,17 +903,54 @@ def wx_get_template_by_intent(): 只返回分类名称,不要其他任何内容。""" - # 调用意图识别 - response = client.chat.completions.create( - model="deepseek-chat", - messages=[ - {"role": "system", "content": intent_system_prompt}, - {"role": "user", "content": user_input} - ], - temperature=0.1 - ) + # 开发环境模拟API响应(当API密钥无效时) + api_key = current_app.config.get('LLM_API_KEY', '') + print(f"\n=== 意图识别API密钥: {api_key} ===") - intent = response.choices[0].message.content.strip() + if api_key in ['test-api-key', 'your-actual-api-key-here', 'sk-your-api-key-here'] or 'test' in api_key.lower() or 'your-api-key' in api_key.lower(): + # 模拟意图识别响应 + mock_intents = { + "写代码": "代码开发", + "开发": "代码开发", + "编程": "代码开发", + "网站": "网站开发", + "网页": "网站开发", + "设计": "产品设计", + "UI": "产品设计", + "界面": "产品设计", + "图片": "生成图片", + "图像": "生成图片", + "新闻": "新闻获取", + "资讯": "新闻获取", + "文案": "文案创作", + "营销": "市场营销", + "推广": "市场营销", + "数据": "数据分析", + "分析": "数据分析" + } + + # 根据输入文本返回相应的模拟意图 + for key, value in mock_intents.items(): + if key in user_input: + intent = value + print(f"\n=== 模拟意图识别结果: {intent} ===") + break + else: + intent = "其它" + print(f"\n=== 模拟意图识别结果: {intent} ===") + else: + # 真实API调用 + client = get_openai_client() + response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": intent_system_prompt}, + {"role": "user", "content": user_input} + ], + temperature=0.1 + ) + + intent = response.choices[0].message.content.strip() # 根据意图获取对应的模板提示词 intent_prompts = { @@ -966,17 +1056,81 @@ def wx_generate_expert_prompt(): 6. 不要添加任何额外的文本""" try: - # 获取意图分析结果 - intent_response = client.chat.completions.create( - model="deepseek-chat", - messages=[ - {"role": "system", "content": intent_analyst_prompt}, - {"role": "user", "content": user_input} - ], - temperature=0.1 # 降低温度,使输出更确定 - ) + # 开发环境模拟API响应(当API密钥无效时) + api_key = current_app.config.get('LLM_API_KEY', '') + print(f"\n=== 专家意图分析API密钥: {api_key} ===") - intent_analysis_text = intent_response.choices[0].message.content.strip() + if api_key in ['test-api-key', 'your-actual-api-key-here', 'sk-your-api-key-here'] or 'test' in api_key.lower() or 'your-api-key' in api_key.lower(): + # 模拟意图分析响应 + mock_intent_analysis = { + "core_intent": "技术", + "domain": "软件开发", + "key_requirements": [ + "功能完整性", + "性能优化", + "代码质量" + ], + "expected_output": "可执行的代码解决方案", + "constraints": [ + "遵循最佳实践", + "考虑可维护性" + ], + "keywords": [ + "编程", + "开发", + "技术" + ] + } + + # 根据输入文本调整分析结果 + print(f"\n=== 输入文本分析: {user_input} ===") + + if any(keyword in user_input for keyword in ["设计", "UI", "界面", "用户体验", "视觉", "交互", "原型", "界面设计", "UI设计"]): + mock_intent_analysis["core_intent"] = "创意" + mock_intent_analysis["domain"] = "产品设计" + mock_intent_analysis["key_requirements"] = ["用户体验", "视觉设计", "交互设计"] + mock_intent_analysis["expected_output"] = "设计方案和原型" + mock_intent_analysis["keywords"] = ["设计", "UI", "用户体验"] + print("=== 匹配到创意类型 ===") + elif any(keyword in user_input for keyword in ["分析", "数据", "统计", "报告", "洞察", "趋势", "图表", "可视化", "挖掘"]): + mock_intent_analysis["core_intent"] = "分析" + mock_intent_analysis["domain"] = "数据分析" + mock_intent_analysis["key_requirements"] = ["数据准确性", "分析深度", "洞察价值"] + mock_intent_analysis["expected_output"] = "分析报告和建议" + mock_intent_analysis["keywords"] = ["分析", "数据", "洞察"] + print("=== 匹配到分析类型 ===") + elif any(keyword in user_input for keyword in ["咨询", "建议", "方案", "策略", "规划", "优化", "改进", "评估", "诊断"]): + mock_intent_analysis["core_intent"] = "咨询" + mock_intent_analysis["domain"] = "业务咨询" + mock_intent_analysis["key_requirements"] = ["专业建议", "可行性分析", "实施方案"] + mock_intent_analysis["expected_output"] = "咨询报告和方案" + mock_intent_analysis["keywords"] = ["咨询", "建议", "方案"] + print("=== 匹配到咨询类型 ===") + elif any(keyword in user_input for keyword in ["代码", "编程", "开发", "技术", "系统", "软件", "应用", "程序", "算法", "架构"]): + mock_intent_analysis["core_intent"] = "技术" + mock_intent_analysis["domain"] = "软件开发" + mock_intent_analysis["key_requirements"] = ["功能完整性", "性能优化", "代码质量"] + mock_intent_analysis["expected_output"] = "可执行的代码解决方案" + mock_intent_analysis["keywords"] = ["编程", "开发", "技术"] + print("=== 匹配到技术类型 ===") + else: + print("=== 未匹配到特定类型,使用默认技术类型 ===") + + intent_analysis_text = json.dumps(mock_intent_analysis, ensure_ascii=False, indent=2) + print(f"\n=== 模拟意图分析结果: {intent_analysis_text} ===") + else: + # 真实API调用 + client = get_openai_client() + intent_response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": intent_analyst_prompt}, + {"role": "user", "content": user_input} + ], + temperature=0.1 # 降低温度,使输出更确定 + ) + + intent_analysis_text = intent_response.choices[0].message.content.strip() # 添加日志记录 current_app.logger.info(f"AI返回的意图分析结果: {intent_analysis_text}") @@ -1111,19 +1265,176 @@ def wx_generate_expert_prompt(): ) try: - # 生成最终提示词 - final_response = client.chat.completions.create( - model="deepseek-chat", - messages=[ - {"role": "system", "content": expert_prompt.format( - analysis=json.dumps(intent_analysis, ensure_ascii=False, indent=2) - )}, - {"role": "user", "content": user_input} - ], - temperature=0.7 - ) + # 开发环境模拟API响应(当API密钥无效时) + api_key = current_app.config.get('LLM_API_KEY', '') + print(f"\n=== 专家提示词生成API密钥: {api_key} ===") - generated_prompt = final_response.choices[0].message.content.strip() + if api_key in ['test-api-key', 'your-actual-api-key-here', 'sk-your-api-key-here'] or 'test' in api_key.lower() or 'your-api-key' in api_key.lower(): + # 模拟专家提示词生成响应 + core_intent = intent_analysis.get('core_intent', '技术') + domain = intent_analysis.get('domain', '软件开发') + + mock_expert_prompts = { + "技术": f"""基于您的需求,我为您生成一个专业的技术任务提示词: + +**技术背景和上下文:** +您需要在{domain}领域完成一项技术任务,需要确保技术方案的可行性和实用性。 + +**技术要求和规范:** +1. 遵循行业最佳实践和标准 +2. 确保代码质量和可维护性 +3. 考虑性能和安全性要求 +4. 提供完整的实现方案 + +**性能和质量标准:** +- 代码执行效率优化 +- 错误处理和异常管理 +- 文档和注释完整性 +- 测试覆盖率要求 + +**技术约束条件:** +- 技术栈兼容性 +- 资源使用限制 +- 时间进度要求 +- 团队协作规范 + +**预期交付成果:** +- 完整的技术实现方案 +- 详细的代码示例 +- 部署和配置说明 +- 测试验证方案 + +**评估标准:** +- 功能完整性验证 +- 性能指标达标 +- 代码质量审查 +- 用户体验评估""", + + "创意": f"""基于您的创意需求,我为您生成一个专业的创意设计提示词: + +**创意方向和灵感来源:** +在{domain}领域探索创新的设计理念,结合用户需求和市场趋势,创造独特的视觉体验。 + +**风格和氛围要求:** +1. 现代简约的设计风格 +2. 温暖友好的用户界面 +3. 专业可信的品牌形象 +4. 富有创意的视觉元素 + +**目标受众定义:** +- 明确用户群体特征 +- 理解用户行为习惯 +- 分析用户需求痛点 +- 设计用户旅程地图 + +**设计元素规范:** +- 色彩搭配和视觉层次 +- 字体选择和排版规范 +- 图标和插画风格 +- 布局和空间设计 + +**创意表现形式:** +- 交互设计创新 +- 动效和过渡效果 +- 响应式设计适配 +- 无障碍设计考虑 + +**评估标准:** +- 视觉吸引力评估 +- 用户体验测试 +- 品牌一致性检查 +- 创新性价值验证""", + + "分析": f"""基于您的分析需求,我为您生成一个专业的数据分析提示词: + +**分析目标和范围:** +在{domain}领域进行深入的数据分析,挖掘有价值的信息洞察,为决策提供数据支撑。 + +**数据要求和规范:** +1. 数据来源的可靠性和完整性 +2. 数据格式和结构标准化 +3. 数据清洗和预处理要求 +4. 数据安全和隐私保护 + +**分析方法和工具:** +- 统计分析方法和模型选择 +- 数据可视化工具和技术 +- 机器学习算法应用 +- 业务指标和KPI定义 + +**输出格式要求:** +- 分析报告的结构和内容 +- 图表和可视化展示 +- 关键发现和建议总结 +- 可操作的洞察建议 + +**关键指标定义:** +- 核心业务指标监控 +- 趋势分析和预测模型 +- 异常检测和预警机制 +- 效果评估和ROI分析 + +**质量控制标准:** +- 数据准确性验证 +- 分析逻辑合理性 +- 结果可解释性 +- 建议可操作性""", + + "咨询": f"""基于您的咨询需求,我为您生成一个专业的咨询服务提示词: + +**咨询问题界定:** +在{domain}领域提供专业的咨询服务,帮助您解决业务挑战,制定有效的解决方案。 + +**背景信息要求:** +1. 当前业务状况和挑战 +2. 目标和期望结果 +3. 资源和约束条件 +4. 时间进度要求 + +**分析框架设定:** +- 问题分析和诊断方法 +- 市场调研和竞争分析 +- 风险评估和管理策略 +- 成本效益分析模型 + +**建议输出格式:** +- 咨询报告的结构和内容 +- 解决方案的详细说明 +- 实施计划和里程碑 +- 预期效果和评估指标 + +**实施考虑因素:** +- 组织变革管理 +- 团队培训和能力建设 +- 技术支持和维护 +- 持续改进机制 + +**效果评估标准:** +- 目标达成度评估 +- 投资回报率分析 +- 客户满意度调查 +- 长期价值创造""" + } + + generated_prompt = mock_expert_prompts.get(core_intent, mock_expert_prompts["技术"]) + print(f"\n=== 模拟专家提示词生成结果 ===") + print(f"生成的提示词: {generated_prompt[:200]}...") + print("==================\n") + else: + # 真实API调用 + client = get_openai_client() + final_response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": expert_prompt.format( + analysis=json.dumps(intent_analysis, ensure_ascii=False, indent=2) + )}, + {"role": "user", "content": user_input} + ], + temperature=0.7 + ) + + generated_prompt = final_response.choices[0].message.content.strip() except Exception as e: current_app.logger.error(f"生成提示词失败: {str(e)}") diff --git a/start.bat b/start.bat deleted file mode 100644 index 444ce20..0000000 --- a/start.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -.venv\Scripts\uwsgi.exe --ini uwsgi.ini \ No newline at end of file diff --git a/start.sh b/start.sh deleted file mode 100644 index 83b85f7..0000000 --- a/start.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -uwsgi --ini uwsgi.ini \ No newline at end of file diff --git a/start_production.bat b/start_production.bat new file mode 100644 index 0000000..e7b6d58 --- /dev/null +++ b/start_production.bat @@ -0,0 +1,19 @@ +@echo off +chcp 65001 >nul +echo ============================================================ +echo 🚀 Flask 提示词大师 - 生产环境启动脚本 +echo ============================================================ +echo. + +REM 设置环境变量 +set FLASK_ENV=production +set PYTHONPATH=%cd% + +echo 📊 环境变量设置完成 +echo 🌐 启动生产环境服务器... +echo. + +REM 启动生产环境服务器 +python run_production.py + +pause diff --git a/start_production_simple.bat b/start_production_simple.bat new file mode 100644 index 0000000..2f99864 --- /dev/null +++ b/start_production_simple.bat @@ -0,0 +1,22 @@ +@echo off +chcp 65001 >nul +echo ============================================================ +echo 🚀 Flask 提示词大师 - 简单生产环境启动脚本 +echo ============================================================ +echo. + +REM 激活虚拟环境 +call .venv\Scripts\Activate.bat + +REM 设置环境变量 +set FLASK_ENV=production +set PYTHONPATH=%cd% + +echo 📊 环境变量设置完成 +echo 🌐 启动生产环境服务器... +echo. + +REM 启动生产环境服务器 +python run_production_simple.py + +pause diff --git a/stop.bat b/stop.bat deleted file mode 100644 index 3053933..0000000 --- a/stop.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -.venv\Scripts\uwsgi.exe --stop uwsgi.pid \ No newline at end of file diff --git a/stop.sh b/stop.sh deleted file mode 100644 index 6b6998b..0000000 --- a/stop.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -uwsgi --stop uwsgi.pid \ No newline at end of file diff --git a/stop_production.bat b/stop_production.bat new file mode 100644 index 0000000..00445fd --- /dev/null +++ b/stop_production.bat @@ -0,0 +1,18 @@ +@echo off +chcp 65001 >nul +echo ============================================================ +echo 🛑 Flask 提示词大师 - 生产环境停止脚本 +echo ============================================================ +echo. + +echo 🔍 查找并停止 Python 进程... +echo. + +REM 查找并停止 Python 进程 +tasklist /FI "IMAGENAME eq python.exe" /FO TABLE +echo. +echo ⚠️ 请手动关闭相关 Python 进程 +echo 💡 或者按 Ctrl+C 停止服务器 +echo. + +pause diff --git a/tests/conftest.py b/tests/conftest.py index c932854..5e185ba 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,24 +1,96 @@ import pytest import os -import sys +import tempfile +from dotenv import load_dotenv -# 添加项目根目录到Python路径 -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +# 加载测试环境变量 +load_dotenv('env.test') -# 测试数据库配置 -@pytest.fixture(scope="session") -def test_db(): - # 这里可以设置测试数据库的配置 +@pytest.fixture(scope='session') +def app(): + """创建测试应用实例""" + from src.flask_prompt_master import create_app + from config import TestingConfig + + # 创建临时数据库文件 + db_fd, db_path = tempfile.mkstemp() + + # 设置测试环境变量 + os.environ['FLASK_ENV'] = 'testing' + os.environ['DATABASE_URL'] = f'sqlite:///{db_path}' + + app = create_app(TestingConfig) + + # 创建数据库表 + with app.app_context(): + from src.flask_prompt_master import db + db.create_all() + + # 创建测试数据 + create_test_data(db) + + yield app + + # 清理 + os.close(db_fd) + os.unlink(db_path) + +@pytest.fixture +def client(app): + """创建测试客户端""" + return app.test_client() + +@pytest.fixture +def runner(app): + """创建测试运行器""" + return app.test_cli_runner() + +def create_test_data(db): + """创建测试数据""" + from src.flask_prompt_master.models import PromptTemplate, User, WxUser + + # 创建测试模板 + test_template = PromptTemplate( + name='测试模板', + description='这是一个测试模板', + category='软件开发', + industry='互联网', + profession='开发工程师', + sub_category='后端开发', + system_prompt='你是一个专业的后端开发工程师...', + is_default=True + ) + db.session.add(test_template) + + # 创建测试用户 + test_user = User( + username='testuser', + email='test@example.com' + ) + db.session.add(test_user) + + # 创建测试微信用户 + test_wx_user = WxUser( + openid='test_openid_123', + session_key='test_session_key_123', + nickname='测试用户', + avatar_url='https://example.com/avatar.jpg' + ) + db.session.add(test_wx_user) + + db.session.commit() + +@pytest.fixture +def auth_headers(): + """认证头信息""" return { - "host": "localhost", - "database": "test_prompt_template", - "user": "test_user", - "password": "test_password" + 'Authorization': 'Bearer test_token_123', + 'Content-Type': 'application/json' } -# 测试客户端配置 -@pytest.fixture(scope="session") -def test_client(): - from flask_prompt_master import create_app - app = create_app('testing') - return app.test_client() \ No newline at end of file +@pytest.fixture +def wx_auth_headers(): + """微信认证头信息""" + return { + 'Content-Type': 'application/json' + } \ No newline at end of file diff --git a/uwsgi.ini b/uwsgi.ini index d7c716a..6d91ada 100644 --- a/uwsgi.ini +++ b/uwsgi.ini @@ -1,12 +1,12 @@ [uwsgi] # 项目根目录 -chdir = /path/to/your/project # 替换为你的项目路径,例如 D:/wxxcx/tsccc +chdir = %(here) # Python 虚拟环境 -virtualenv = .venv # 相对于项目根目录的虚拟环境路径 +virtualenv = .venv # uwsgi 启动文件 -wsgi-file = app.py +wsgi-file = run_dev.py # Flask 应用实例 callable = app @@ -24,16 +24,32 @@ threads = 2 http = 0.0.0.0:5000 # 后台运行 -# daemonize = uwsgi.log +# daemonize = logs/uwsgi.log # 日志文件 logto = logs/uwsgi.log # pid文件,用于停止服务 -pidfile = uwsgi.pid +pidfile = logs/uwsgi.pid # 自动重载 py-autoreload = 1 # 缓冲区大小 -buffer-size = 32768 \ No newline at end of file +buffer-size = 32768 + +# 环境变量设置 +env = FLASK_ENV=production + +# 内存限制 +memory-report = true +max-requests = 1000 +max-requests-delta = 100 + +# 优雅重启 +reload-on-rss = 2048 +reload-on-as = 512 + +# 日志级别 +log-maxsize = 50000000 +log-backupname = logs/uwsgi.log.bak \ No newline at end of file