diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 2a4bf4e..c7768c5 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -28,7 +28,11 @@ "Bash(timeout /t 3 /nobreak)", "Bash(ping:*)", "Bash(git diff-tree:*)", - "Bash(git format-patch:*)" + "Bash(git format-patch:*)", + "WebFetch(domain:bocha-ai.feishu.cn)", + "Bash(ls:*)", + "Bash(git pull:*)", + "Bash(del nul)" ] } } diff --git a/1PANEL_DEPLOY.md b/1PANEL_DEPLOY.md new file mode 100644 index 0000000..f9e05ac --- /dev/null +++ b/1PANEL_DEPLOY.md @@ -0,0 +1,358 @@ +# 1Panel Web界面部署指南 + +## 使用1Panel Web界面部署ZJPB项目 + +### 前提条件 +- 1Panel已安装并可访问 +- 1Panel版本支持Python项目管理 + +--- + +## 方式一:使用1Panel的运行时环境(推荐) + +### 步骤1:准备项目文件 + +1. **压缩项目** + - 在本地压缩整个项目文件夹 + - 排除:`venv/`, `__pycache__/`, `.env`, `test_*.py`, `logs/` + +### 步骤2:在1Panel中创建数据库 + +1. 登录1Panel管理面板 +2. 进入 **数据库** 菜单 +3. 点击 **创建数据库** +4. 填写信息: + ``` + 数据库名:ai_nav + 用户名:ai_nav_user + 密码:(自动生成或自定义) + 权限:本地访问 + ``` +5. 点击确定,**记录数据库密码** + +### 步骤3:上传项目文件 + +1. 进入 **文件** 菜单 +2. 导航到网站目录(如 `/www/wwwroot/`) +3. 创建项目目录 `zjpb` +4. 上传并解压 `zjpb.zip` + +### 步骤4:配置环境变量 + +1. 在项目目录中,找到 `.env.example` 文件 +2. 复制为 `.env` +3. 点击编辑,填写配置: + +```env +# 数据库配置 +DB_HOST=localhost +DB_PORT=3306 +DB_USER=ai_nav_user +DB_PASSWORD=你的数据库密码 +DB_NAME=ai_nav + +# 安全配置 +SECRET_KEY=你的密钥(使用下方命令生成) +FLASK_ENV=production + +# DeepSeek API(可选) +DEEPSEEK_API_KEY=sk-xxxxx +DEEPSEEK_BASE_URL=https://api.deepseek.com +``` + +生成SECRET_KEY: +```bash +# 在1Panel终端执行 +python3 -c "import secrets; print(secrets.token_hex(32))" +``` + +### 步骤5:使用1Panel创建Python项目 + +#### 5.1 进入网站管理 + +1. 点击 **网站** 菜单 +2. 点击 **创建网站** +3. 选择 **运行时** 类型 + +#### 5.2 配置Python项目 + +填写以下信息: + +**基本设置:** +``` +网站类型:运行时 +运行时:Python +域名:your-domain.com(或IP地址) +代码目录:/www/wwwroot/zjpb +``` + +**Python设置:** +``` +Python版本:选择 3.8+ 的版本 +应用类型:选择 "Flask" 或 "其他" +启动文件:app.py +启动命令:gunicorn -c gunicorn_config.py app:app +端口:5000(默认) +``` + +**环境变量:**(如果1Panel支持在界面配置) +``` +FLASK_ENV=production +``` + +**其他选项:** +``` +☑ 自动启动 +☑ 守护进程 +进程数:4 +``` + +#### 5.3 安装依赖 + +1. 创建网站后,1Panel会自动创建虚拟环境 +2. 进入网站设置 +3. 找到 **依赖管理** 或 **包管理** +4. 上传 `requirements.txt` 或手动安装 +5. 点击 **安装依赖** + +或者使用1Panel的终端: +```bash +# 进入项目目录 +cd /www/wwwroot/zjpb + +# 激活虚拟环境(1Panel自动创建的) +source venv/bin/activate + +# 安装依赖 +pip install -r requirements.txt +``` + +### 步骤6:初始化数据库 + +1. 在1Panel中打开 **终端** 或 SSH连接 +2. 执行: +```bash +cd /www/wwwroot/zjpb +source venv/bin/activate +python init_db.py +``` + +### 步骤7:启动和管理 + +1. 在1Panel网站列表中找到你的项目 +2. 点击 **启动** 按钮 +3. 查看状态:运行中 ✓ + +### 步骤8:配置反向代理(如果需要) + +如果1Panel没有自动配置Nginx: + +1. 进入网站设置 +2. 找到 **反向代理** 或 **代理配置** +3. 配置: + ``` + 目标地址:http://127.0.0.1:5000 + ``` + +### 步骤9:配置SSL证书(推荐) + +1. 在网站设置中找到 **SSL** +2. 选择 **Let's Encrypt** +3. 点击申请证书 +4. 启用 **强制HTTPS** + +### 步骤10:访问验证 + +1. 前台:`https://your-domain.com` +2. 后台:`https://your-domain.com/admin/login` + - 默认用户名:`admin` + - 默认密码:`admin123` +3. **立即修改密码**:访问 `/admin/change-password` + +--- + +## 方式二:使用1Panel的OpenResty/Nginx + Supervisor + +如果1Panel的Python运行时不支持或不稳定,可以使用传统方式: + +### 步骤1-4:同上(数据库、文件上传、环境配置) + +### 步骤5:手动创建虚拟环境 + +```bash +cd /www/wwwroot/zjpb +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +### 步骤6:创建Supervisor配置 + +1. 在1Panel中进入 **容器** 或 **进程管理** +2. 如果有Supervisor功能,创建新任务 + +或手动创建配置文件: +```bash +nano /etc/supervisor/conf.d/zjpb.conf +``` + +内容: +```ini +[program:zjpb] +command=/www/wwwroot/zjpb/venv/bin/gunicorn -c gunicorn_config.py app:app +directory=/www/wwwroot/zjpb +user=www +autostart=true +autorestart=true +redirect_stderr=true +stdout_logfile=/www/wwwroot/zjpb/logs/supervisor.log +environment=FLASK_ENV="production" +``` + +启动: +```bash +supervisorctl reread +supervisorctl update +supervisorctl start zjpb +``` + +### 步骤7:创建反向代理网站 + +1. 在1Panel **网站** 菜单中点击 **创建网站** +2. 选择 **反向代理** +3. 填写: + ``` + 域名:your-domain.com + 代理地址:http://127.0.0.1:5000 + ``` + +### 步骤8:配置SSL + +同方式一 + +--- + +## 常见问题 + +### Q1: 1Panel在哪里创建Python项目? + +**A:** +- 进入 **网站** 菜单 +- 点击 **创建网站** +- 选择 **运行时** 或 **Runtime** +- 选择 **Python** + +### Q2: 找不到虚拟环境? + +**A:** +- 1Panel创建的虚拟环境通常在项目目录下的 `venv/` 文件夹 +- 路径:`/www/wwwroot/zjpb/venv/` +- 可以在文件管理器中查看 + +### Q3: 如何查看应用日志? + +**A:** +- 方式1:在网站管理中点击 **日志** 按钮 +- 方式2:查看文件 `logs/error.log` +- 方式3:使用1Panel的终端:`tail -f logs/error.log` + +### Q4: 如何重启应用? + +**A:** +- 方式1:在1Panel网站列表中点击 **重启** 按钮 +- 方式2:使用管理脚本:`./manage.sh restart` +- 方式3:Supervisor:`supervisorctl restart zjpb` + +### Q5: 端口被占用怎么办? + +**A:** +修改 `gunicorn_config.py` 中的端口: +```python +bind = "0.0.0.0:5001" # 改为其他端口 +``` +然后在反向代理中也修改为对应端口。 + +--- + +## 管理和维护 + +### 更新代码 + +1. 在1Panel文件管理中上传新文件 +2. 在网站管理中点击 **重启** + +### 查看状态 + +1. 进入网站列表 +2. 查看状态指示灯 +3. 点击网站名称查看详细信息 + +### 备份数据库 + +1. 进入 **数据库** 菜单 +2. 找到 `ai_nav` 数据库 +3. 点击 **备份** 按钮 + +### 监控日志 + +1. 在网站设置中找到 **日志** 选项 +2. 查看访问日志和错误日志 +3. 可以设置日志轮转 + +--- + +## 推荐配置 + +### 生产环境推荐配置 + +``` +服务器配置: +- CPU: 2核+ +- 内存: 2GB+ +- 硬盘: 20GB+ + +Python版本: +- Python 3.8+ + +数据库: +- MySQL 5.7+ +- MariaDB 10.3+ + +Web服务器: +- Nginx (1Panel自带) + +进程管理: +- Supervisor 或 1Panel内置 + +工作进程数: +- gunicorn workers: 4 +- gunicorn threads: 2 +``` + +--- + +## 安全建议 + +1. ✅ 修改默认管理员密码 +2. ✅ 使用强密码的SECRET_KEY +3. ✅ 启用HTTPS (Let's Encrypt) +4. ✅ 定期备份数据库 +5. ✅ 设置文件权限: + ```bash + chmod 600 .env + chmod 755 static/uploads + ``` +6. ✅ 配置防火墙(1Panel通常自动配置) + +--- + +## 获取帮助 + +如果遇到问题: +1. 查看 `logs/error.log` 日志文件 +2. 检查1Panel的系统日志 +3. 验证数据库连接 +4. 检查端口是否被占用 +5. 确认虚拟环境依赖已安装 + +祝部署顺利!🎉 diff --git a/1PANEL_QUICK.md b/1PANEL_QUICK.md new file mode 100644 index 0000000..7ff9f8b --- /dev/null +++ b/1PANEL_QUICK.md @@ -0,0 +1,180 @@ +# 1Panel快速部署向导 + +## 🎯 使用1Panel Web界面 - 5步完成部署 + +### 📋 准备工作 +- [x] 1Panel已安装 +- [x] 项目文件已压缩(zjpb.zip) + +--- + +## 第1步:创建数据库(2分钟) + +1. 登录1Panel → **数据库** +2. 点击 **创建数据库** +3. 填写: + - 数据库名:`ai_nav` + - 用户名:`ai_nav_user` + - 密码:自动生成(**记录下来!**) +4. 确定 + +--- + +## 第2步:上传项目(3分钟) + +1. **文件** → 导航到 `/www/wwwroot/` +2. 创建文件夹 `zjpb` +3. 上传 `zjpb.zip` +4. 解压缩 +5. 编辑 `.env.example` → 另存为 `.env` +6. 填写数据库密码和密钥 + +--- + +## 第3步:创建Python网站(5分钟) + +### 方式A:使用1Panel的Python运行时(推荐) + +1. **网站** → **创建网站** +2. 类型:**运行时 (Runtime)** +3. 配置: + ``` + 运行时:Python 3.8+ + 应用类型:Flask + 域名:your-domain.com + 代码目录:/www/wwwroot/zjpb + 启动文件:app.py + 启动命令:gunicorn -c gunicorn_config.py app:app + 端口:5000 + ☑ 自动启动 + ``` +4. 创建 + +### 方式B:使用反向代理(备选) + +如果没有Python运行时选项: + +1. **网站** → **创建网站** +2. 类型:**反向代理** +3. 配置: + ``` + 域名:your-domain.com + 代理地址:http://127.0.0.1:5000 + ``` + +然后SSH到服务器手动启动(参考完整文档) + +--- + +## 第4步:安装依赖和初始化(5分钟) + +### 4.1 打开1Panel终端 + +点击1Panel右上角 **终端** 图标,或SSH连接 + +### 4.2 安装依赖 + +```bash +cd /www/wwwroot/zjpb + +# 激活1Panel创建的虚拟环境 +source venv/bin/activate + +# 安装依赖 +pip install -r requirements.txt +``` + +### 4.3 初始化数据库 + +```bash +python init_db.py +``` + +看到成功提示后,记住默认账号: +- 用户名:`admin` +- 密码:`admin123` + +--- + +## 第5步:启动和访问(2分钟) + +### 5.1 启动应用 + +**在1Panel界面:** +1. 进入 **网站** 列表 +2. 找到你的项目 +3. 点击 **启动** 按钮 +4. 状态变为 **运行中 ✓** + +### 5.2 配置SSL(推荐) + +1. 点击网站名称进入设置 +2. 找到 **SSL** 选项 +3. 选择 **Let's Encrypt** +4. 申请证书 +5. 启用 **强制HTTPS** + +### 5.3 访问网站 + +- 前台:`https://your-domain.com` +- 后台:`https://your-domain.com/admin/login` + +### 5.4 修改密码(重要!) + +1. 登录后台(admin / admin123) +2. 点击左侧菜单 **修改密码** +3. 设置新密码 + +--- + +## ✅ 完成! + +现在你的网站已经部署完成并运行了! + +--- + +## 🔧 常用操作 + +### 重启应用 +- 1Panel界面:网站列表 → 点击 **重启** + +### 查看日志 +- 1Panel界面:网站设置 → **日志** +- 或查看文件:`/www/wwwroot/zjpb/logs/error.log` + +### 备份数据库 +- 1Panel界面:数据库 → 选择 `ai_nav` → **备份** + +### 更新代码 +1. 上传新文件到项目目录 +2. 重启应用 + +--- + +## ❓ 常见问题 + +**Q: 找不到Python运行时选项?** +- 使用反向代理方式,然后手动启动应用 +- 参考:`1PANEL_DEPLOY.md` 方式二 + +**Q: 虚拟环境在哪?** +- 1Panel自动创建在:`/www/wwwroot/zjpb/venv/` + +**Q: 启动失败?** +1. 查看日志:`logs/error.log` +2. 检查 `.env` 配置 +3. 确认数据库连接 +4. 验证依赖已安装 + +**Q: 需要修改端口?** +- 编辑 `gunicorn_config.py`,修改 `bind` 参数 + +--- + +## 📚 更多帮助 + +- 完整文档:`1PANEL_DEPLOY.md` +- 传统部署:`DEPLOYMENT.md` +- 检查清单:`DEPLOY_CHECKLIST.md` + +祝部署顺利!🚀 diff --git a/CHECK_GIT.md b/CHECK_GIT.md new file mode 100644 index 0000000..a434438 --- /dev/null +++ b/CHECK_GIT.md @@ -0,0 +1,92 @@ +# 检查生产服务器Git配置 + +## 方法1:通过SSH命令检查 + +```bash +# SSH登录到生产服务器 +ssh root@your-server-ip + +# 检查git是否安装 +git --version + +# 如果显示版本号,说明已安装,例如: +# git version 2.30.2 + +# 如果显示 "command not found",说明未安装 +``` + +## 方法2:通过1Panel面板检查 + +```bash +# 1. 登录1Panel管理面板 +# 2. 进入 "主机" -> "终端" +# 3. 输入命令: +git --version +``` + +## 方法3:检查项目目录是否有.git + +```bash +# SSH登录后 +cd /www/wwwroot/zjpb +ls -la | grep .git + +# 如果显示 .git 目录,说明项目已经是git仓库 +# 可以直接使用 git pull +``` + +--- + +## 如果Git未安装 + +### CentOS/RHEL系统: +```bash +yum install -y git +``` + +### Ubuntu/Debian系统: +```bash +apt update +apt install -y git +``` + +### 验证安装: +```bash +git --version +``` + +--- + +## 如果项目目录没有.git(需要初始化) + +```bash +cd /www/wwwroot/zjpb + +# 初始化git仓库 +git init + +# 添加远程仓库(如果你有GitHub/Gitee等) +git remote add origin https://github.com/yourusername/zjpb.git + +# 或者使用SSH方式 +git remote add origin git@github.com:yourusername/zjpb.git + +# 拉取代码 +git pull origin master +``` + +--- + +## 推荐:使用Git部署的优势 + +✅ **版本控制** - 可以随时回滚到之前的版本 +✅ **增量更新** - 只传输修改的文件,速度快 +✅ **操作简单** - 一条命令完成更新 +✅ **团队协作** - 多人开发更方便 + +## 如果不用Git,手动部署也可以 + +可以使用FTP/SFTP工具上传文件: +- FileZilla(免费) +- WinSCP(免费) +- 1Panel自带的文件管理器 diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..291cbbd --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,463 @@ +# ZJPB - 焦提示词 部署文档 + +## 1Panel部署指南 + +### 前置要求 +- 1Panel管理面板已安装 +- MySQL 5.7+ 或 MariaDB +- Python 3.8+ +- Nginx(1Panel自带) + +--- + +## 一、数据库准备 + +### 1.1 在1Panel中创建MySQL数据库 + +1. 登录1Panel管理面板 +2. 进入「数据库」菜单 +3. 点击「创建数据库」 +4. 填写信息: + - 数据库名:`ai_nav` + - 用户名:`ai_nav_user` + - 密码:自动生成或自定义(记录下来) + - 权限:本地访问 +5. 点击创建 + +### 1.2 导入数据库结构 + +使用1Panel的phpMyAdmin或命令行导入: + +```sql +-- 如果你有数据库备份文件,可以直接导入 +-- 否则在部署后通过init_db.py初始化 +``` + +--- + +## 二、上传项目文件 + +### 2.1 压缩项目 + +在本地Windows环境压缩项目文件夹为 `zjpb.zip`,**排除以下文件/文件夹**: +- `__pycache__/` +- `*.pyc` +- `.git/` +- `.env`(生产环境重新配置) +- `venv/` 或 `env/` +- `test_*.py`(测试文件) + +### 2.2 上传到服务器 + +1. 在1Panel中进入「文件」菜单 +2. 导航到 `/opt/1panel/apps/` 或你的网站目录(如 `/www/wwwroot/`) +3. 创建项目目录:`zjpb` +4. 上传 `zjpb.zip` 并解压 +5. 最终路径示例:`/www/wwwroot/zjpb/` + +--- + +## 三、环境配置 + +### 3.1 SSH连接到服务器 + +使用1Panel的终端或SSH工具连接服务器 + +### 3.2 创建Python虚拟环境 + +```bash +cd /www/wwwroot/zjpb + +# 创建虚拟环境 +python3 -m venv venv + +# 激活虚拟环境 +source venv/bin/activate + +# 升级pip +pip install --upgrade pip +``` + +### 3.3 安装Python依赖 + +```bash +pip install -r requirements.txt +``` + +### 3.4 配置环境变量 + +创建生产环境的 `.env` 文件: + +```bash +nano .env +``` + +填写以下内容(**修改为实际值**): + +```env +# 数据库配置 +DB_HOST=localhost +DB_PORT=3306 +DB_USER=ai_nav_user +DB_PASSWORD=你的数据库密码 +DB_NAME=ai_nav + +# 安全配置(生成随机密钥) +SECRET_KEY=your-production-secret-key-change-this + +# 运行环境 +FLASK_ENV=production + +# DeepSeek API配置(可选) +DEEPSEEK_API_KEY=你的DeepSeek_API密钥 +DEEPSEEK_BASE_URL=https://api.deepseek.com +``` + +**生成安全的SECRET_KEY**: +```bash +python3 -c "import secrets; print(secrets.token_hex(32))" +``` + +### 3.5 创建必要的目录 + +```bash +# 创建日志目录 +mkdir -p logs + +# 创建静态文件上传目录 +mkdir -p static/uploads + +# 设置权限 +chmod 755 logs static/uploads +``` + +--- + +## 四、初始化数据库 + +### 4.1 运行初始化脚本 + +```bash +source venv/bin/activate +python init_db.py +``` + +这会创建所有表并创建默认管理员账号: +- 用户名:`admin` +- 密码:`admin123` + +**⚠️ 重要:登录后立即修改默认密码!** + +--- + +## 五、配置Nginx反向代理 + +### 5.1 在1Panel中创建网站 + +1. 进入1Panel「网站」菜单 +2. 点击「创建网站」 +3. 填写信息: + - 网站类型:反向代理 + - 域名:`your-domain.com`(或IP) + - 代理地址:`http://127.0.0.1:5000` + - 启用SSL:推荐(自动申请Let's Encrypt证书) + +### 5.2 自定义Nginx配置(可选) + +如果需要自定义,编辑Nginx配置: + +```nginx +server { + listen 80; + server_name your-domain.com; + + # 如果启用SSL,这里会自动重定向到443 + + client_max_body_size 10M; # 允许上传文件大小 + + location / { + proxy_pass http://127.0.0.1:5000; + 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; + + # WebSocket支持(如果需要) + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # 静态文件直接由Nginx处理 + location /static/ { + alias /www/wwwroot/zjpb/static/; + expires 30d; + add_header Cache-Control "public, immutable"; + } +} +``` + +--- + +## 六、配置Supervisor守护进程 + +### 6.1 在1Panel中使用Supervisor + +1. 进入1Panel「容器」→「应用编排」或「进程管理」 +2. 创建新的守护进程配置 + +### 6.2 创建Supervisor配置文件 + +如果1Panel没有内置,手动创建: + +```bash +nano /etc/supervisor/conf.d/zjpb.conf +``` + +配置内容: + +```ini +[program:zjpb] +command=/www/wwwroot/zjpb/venv/bin/gunicorn -c gunicorn_config.py app:app +directory=/www/wwwroot/zjpb +user=www +autostart=true +autorestart=true +redirect_stderr=true +stdout_logfile=/www/wwwroot/zjpb/logs/supervisor.log +environment=FLASK_ENV="production" +``` + +### 6.3 启动服务 + +```bash +# 重新加载Supervisor配置 +supervisorctl reread +supervisorctl update + +# 启动应用 +supervisorctl start zjpb + +# 查看状态 +supervisorctl status zjpb +``` + +--- + +## 七、使用systemd守护进程(替代方案) + +如果不使用Supervisor,可以用systemd: + +### 7.1 创建systemd服务文件 + +```bash +sudo nano /etc/systemd/system/zjpb.service +``` + +内容: + +```ini +[Unit] +Description=ZJPB AI Navigation Flask Application +After=network.target mysql.service + +[Service] +Type=notify +User=www +Group=www +WorkingDirectory=/www/wwwroot/zjpb +Environment="PATH=/www/wwwroot/zjpb/venv/bin" +Environment="FLASK_ENV=production" +ExecStart=/www/wwwroot/zjpb/venv/bin/gunicorn -c gunicorn_config.py app:app +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + +### 7.2 启动服务 + +```bash +# 重新加载systemd +sudo systemctl daemon-reload + +# 启动服务 +sudo systemctl start zjpb + +# 设置开机自启 +sudo systemctl enable zjpb + +# 查看状态 +sudo systemctl status zjpb +``` + +--- + +## 八、验证部署 + +### 8.1 检查服务状态 + +```bash +# 查看gunicorn进程 +ps aux | grep gunicorn + +# 查看日志 +tail -f logs/error.log +tail -f logs/access.log +``` + +### 8.2 访问网站 + +1. 前台访问:`http://your-domain.com` +2. 后台访问:`http://your-domain.com/admin/login` + - 用户名:`admin` + - 密码:`admin123`(首次登录后立即修改) + +--- + +## 九、常用管理命令 + +### 9.1 重启应用 + +**使用Supervisor:** +```bash +supervisorctl restart zjpb +``` + +**使用systemd:** +```bash +sudo systemctl restart zjpb +``` + +### 9.2 查看日志 + +```bash +# 应用日志 +tail -f logs/error.log + +# Nginx访问日志 +tail -f /www/server/nginx/logs/your-domain.com.log + +# Supervisor日志 +tail -f logs/supervisor.log +``` + +### 9.3 更新代码 + +```bash +cd /www/wwwroot/zjpb + +# 备份数据库 +mysqldump -u ai_nav_user -p ai_nav > backup_$(date +%Y%m%d).sql + +# 拉取新代码或上传新文件 +# ... + +# 激活虚拟环境 +source venv/bin/activate + +# 安装新依赖 +pip install -r requirements.txt + +# 重启应用 +supervisorctl restart zjpb +# 或 +sudo systemctl restart zjpb +``` + +--- + +## 十、安全加固 + +### 10.1 修改默认管理员密码 + +登录后台后立即修改密码: +1. 访问:`/admin/change-password` +2. 输入旧密码:`admin123` +3. 设置新的强密码 + +### 10.2 配置防火墙 + +```bash +# 只允许80和443端口(1Panel通常已配置) +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp +sudo ufw enable +``` + +### 10.3 配置SSL证书 + +在1Panel中为网站启用SSL: +1. 进入网站设置 +2. 启用SSL +3. 选择Let's Encrypt免费证书 +4. 自动申请并配置 + +--- + +## 十一、故障排查 + +### 11.1 应用无法启动 + +检查日志: +```bash +tail -f logs/error.log +``` + +常见问题: +- 数据库连接失败:检查`.env`配置 +- 端口被占用:修改`gunicorn_config.py`中的端口 +- 权限问题:确保文件所有者为`www`用户 + +### 11.2 静态文件404 + +检查Nginx配置和目录权限: +```bash +ls -la static/ +chmod -R 755 static/ +``` + +### 11.3 数据库连接失败 + +```bash +# 测试数据库连接 +mysql -u ai_nav_user -p ai_nav + +# 检查MySQL服务 +sudo systemctl status mysql +``` + +--- + +## 十二、备份策略 + +### 12.1 数据库备份 + +创建定时任务: +```bash +crontab -e +``` + +添加每日备份: +```cron +0 2 * * * mysqldump -u ai_nav_user -p密码 ai_nav > /www/backup/zjpb_$(date +\%Y\%m\%d).sql +``` + +### 12.2 文件备份 + +```bash +# 备份上传的文件 +tar -czf uploads_backup_$(date +%Y%m%d).tar.gz static/uploads/ +``` + +--- + +## 支持与帮助 + +如有问题,请检查: +1. 应用日志:`logs/error.log` +2. Nginx日志:`/www/server/nginx/logs/` +3. 系统日志:`journalctl -u zjpb -f` + +祝部署顺利!🚀 diff --git a/DEPLOY_CHECKLIST.md b/DEPLOY_CHECKLIST.md new file mode 100644 index 0000000..c25fd0c --- /dev/null +++ b/DEPLOY_CHECKLIST.md @@ -0,0 +1,132 @@ +# 📦 1Panel部署打包清单 + +部署前请确保以下文件都已准备好: + +## ✅ 必需文件 + +### 应用文件 +- [x] app.py - 主应用文件 +- [x] wsgi.py - WSGI入口(生产环境) +- [x] config.py - 配置文件 +- [x] models.py - 数据模型 +- [x] init_db.py - 数据库初始化脚本 +- [x] requirements.txt - Python依赖列表 + +### 配置文件 +- [x] gunicorn_config.py - Gunicorn配置 +- [x] .env.example - 环境变量模板 +- [ ] .env - 生产环境变量(需在服务器上创建) + +### 部署脚本 +- [x] deploy.sh - 一键部署脚本 +- [x] manage.sh - 应用管理脚本 + +### 文档 +- [x] DEPLOYMENT.md - 完整部署文档 +- [x] QUICK_DEPLOY.md - 快速部署指南 +- [x] README.md - 项目说明 + +### 目录结构 +``` +zjpb/ +├── static/ # 静态文件 +│ ├── css/ +│ ├── js/ +│ └── uploads/ # 上传文件目录 +├── templates/ # 模板文件 +│ ├── admin/ +│ └── *.html +├── utils/ # 工具类 +│ ├── website_fetcher.py +│ ├── tag_generator.py +│ └── bookmark_parser.py +├── migrations/ # 数据库迁移脚本 +├── logs/ # 日志目录(自动创建) +└── venv/ # 虚拟环境(服务器上创建) +``` + +## 🚫 排除文件(不要上传到服务器) + +- `__pycache__/` - Python缓存 +- `*.pyc` - 编译的Python文件 +- `.git/` - Git仓库 +- `.env` - 本地环境变量 +- `venv/` `env/` - 虚拟环境 +- `test_*.py` - 测试文件 +- `*.log` - 日志文件 +- `logs/` - 日志目录 +- `static/uploads/*` - 上传的文件 + +## 📋 部署前检查清单 + +### 本地准备 +- [ ] 更新 requirements.txt +- [ ] 测试应用运行正常 +- [ ] 准备 .env.example 模板 +- [ ] 压缩项目文件 + +### 服务器准备 +- [ ] 1Panel已安装 +- [ ] MySQL数据库已创建 +- [ ] 域名已解析(可选) +- [ ] SSH访问权限 + +### 部署步骤 +- [ ] 上传项目文件到服务器 +- [ ] 解压并设置目录权限 +- [ ] 执行 deploy.sh 脚本 +- [ ] 配置 .env 文件 +- [ ] 初始化数据库 +- [ ] 在1Panel中创建网站(反向代理) +- [ ] 启动应用 +- [ ] 配置SSL证书(可选) + +### 部署后验证 +- [ ] 前台页面访问正常 +- [ ] 后台登录成功 +- [ ] 修改默认管理员密码 +- [ ] 测试网站添加功能 +- [ ] 测试标签创建功能 +- [ ] 测试图片上传功能 + +## 🔐 安全检查 + +- [ ] 修改默认管理员密码 +- [ ] 设置强密码的 SECRET_KEY +- [ ] 配置 .env 权限(chmod 600) +- [ ] 启用 SSL/HTTPS +- [ ] 配置防火墙规则 +- [ ] 定期备份数据库 + +## 📝 压缩命令 + +Windows PowerShell: +```powershell +Compress-Archive -Path * -DestinationPath zjpb.zip -Force +``` + +Linux/Mac: +```bash +zip -r zjpb.zip . -x "*.pyc" "*__pycache__*" "*.git*" ".env" "venv/*" "test_*.py" "logs/*" +``` + +## 🎯 快速部署(服务器上) + +```bash +# 1. 上传并解压 +cd /www/wwwroot/zjpb +unzip zjpb.zip + +# 2. 一键部署 +chmod +x deploy.sh +./deploy.sh + +# 3. 配置环境变量 +nano .env + +# 4. 启动应用 +chmod +x manage.sh +./manage.sh start +``` + +详细步骤请参考:`DEPLOYMENT.md` diff --git a/GIT_PATCH_DEPLOY.md b/GIT_PATCH_DEPLOY.md new file mode 100644 index 0000000..4c45046 --- /dev/null +++ b/GIT_PATCH_DEPLOY.md @@ -0,0 +1,232 @@ +# ZJPB v2.1 Git Patch 部署指南(最简单) + +## ✨ 优势 + +- ✅ **最干净** - 使用Git patch,保持版本历史完整 +- ✅ **最安全** - 自动备份未提交的修改 +- ✅ **最快速** - 只需上传2个文件(89KB) +- ✅ **可追溯** - 所有更改都有Git记录 + +--- + +## 📦 需要上传的文件(仅2个) + +1. `v2.1.0.patch` (89KB) - 代码补丁文件 +2. `git_patch_deploy.sh` - 自动化部署脚本 + +--- + +## 🚀 部署步骤 + +### 第一步:备份生产数据库(在1Panel中操作) + +1. 登录1Panel管理面板 +2. 进入 **数据库** → 找到 `ai_nav` 数据库 +3. 点击 **备份** 按钮 +4. 下载备份文件保存 + +### 第二步:上传文件到服务器 + +**方法1:使用1Panel文件管理器** + +1. 登录1Panel +2. 进入 **文件** → 导航到 `/opt/1panel/apps/zjpb/` +3. 上传文件: + - `v2.1.0.patch` + - `git_patch_deploy.sh` + +**方法2:使用命令行(如果你用SSH)** + +```bash +# 在本地(Windows)使用SCP上传 +scp v2.1.0.patch root@your-server-ip:/opt/1panel/apps/zjpb/ +scp git_patch_deploy.sh root@your-server-ip:/opt/1panel/apps/zjpb/ +``` + +### 第三步:执行部署脚本 + +在1Panel终端或SSH中执行: + +```bash +# 进入项目目录 +cd /opt/1panel/apps/zjpb + +# 赋予执行权限 +chmod +x git_patch_deploy.sh + +# 执行部署 +./git_patch_deploy.sh +``` + +**脚本会自动完成:** +1. ✅ 停止应用 +2. ✅ 检查Git状态 +3. ✅ 备份未提交的修改(如有) +4. ✅ 应用v2.1.0补丁 +5. ✅ 提交到Git +6. ✅ 安装依赖 +7. ✅ 运行数据库迁移(创建prompt_templates表) +8. ✅ 重启应用 +9. ✅ 检查状态 + +**预期输出:** +``` +================================ +ZJPB v2.1 Git Patch 部署 +================================ + +当前目录: /opt/1panel/apps/zjpb + +1. 停止应用... +2. 检查Git状态... +3. 备份当前修改(如有)... +4. 应用v2.1.0补丁... + ✅ 补丁应用成功 +5. 提交更改到Git... +6. 激活虚拟环境... +7. 检查依赖... +8. 运行数据库迁移... +正在创建 prompt_templates 表... +[OK] 表创建成功 +[OK] 默认prompt模板初始化成功 +9. 重启应用... +10. 检查应用状态... +zjpb 正在运行 (PID: xxxx) + +================================ +✅ 部署完成! +================================ +``` + +### 第四步:验证部署 + +**前台验证:** +1. 访问首页,检查页脚: + - `© 2025 ZJPB - 焦提示词 | AI工具导航` + - `浙ICP备2025154782号-1` + - 打开F12检查Network,确认Clarity统计已加载 + +2. 访问任意网站详情页,检查图标: + - 返回首页:`←` 而不是 `arrow_back` + - 访问网站:`↗` 而不是 `north_east` + - 浏览次数:`👁` 而不是 `visibility` + +**后台验证:** +1. 登录 `/admin/login` +2. 左侧菜单应该有 **Prompt管理** +3. 点击进入,查看3条默认模板: + - 标签生成 + - 主要功能生成 + - 详细介绍生成 + +4. 进入 **网站管理** → 编辑任意网站 +5. 标签区域应该正常显示标签名称(蓝色标签,有文字) + +**AI功能验证:** +1. 编辑或创建网站 +2. 测试 **AI生成标签** 按钮 +3. 测试 **AI生成详细介绍** 按钮 +4. 测试 **AI生成主要功能** 按钮 + +--- + +## 🔍 部署后检查 + +查看Git提交历史: +```bash +cd /opt/1panel/apps/zjpb +git log --oneline -5 +``` + +应该看到最新的提交: +``` +xxxxxxx release: v2.1.0 - Prompt管理系统、页脚优化、图标修复 +``` + +查看应用状态: +```bash +./manage.sh status +``` + +查看应用日志(如有问题): +```bash +./manage.sh logs +``` + +--- + +## 🔄 回滚方案(如出现问题) + +```bash +cd /opt/1panel/apps/zjpb + +# 停止应用 +./manage.sh stop + +# 回滚到上一个提交 +git reset --hard HEAD~1 + +# 如果有stash的备份,恢复它 +git stash list +git stash pop + +# 重启应用 +./manage.sh start +``` + +--- + +## 📝 注意事项 + +1. ✅ 服务器路径是 `/opt/1panel/apps/zjpb` 不是 `/www/wwwroot/zjpb` +2. ✅ 已确认服务器有Git仓库 +3. ✅ patch文件会自动保存现有未提交的修改 +4. ✅ 部署过程中会自动运行数据库迁移 +5. ✅ 所有更改都会提交到Git,可随时回滚 + +--- + +## 🎯 完整部署命令(复制粘贴) + +```bash +# 1. 进入项目目录 +cd /opt/1panel/apps/zjpb + +# 2. 检查文件是否上传成功 +ls -lh v2.1.0.patch git_patch_deploy.sh + +# 3. 赋予执行权限 +chmod +x git_patch_deploy.sh + +# 4. 执行部署 +./git_patch_deploy.sh + +# 5. 部署完成后验证 +./manage.sh status +git log --oneline -3 +``` + +--- + +## ✅ 部署检查清单 + +- [ ] 生产数据库已备份 +- [ ] v2.1.0.patch 已上传到服务器 +- [ ] git_patch_deploy.sh 已上传到服务器 +- [ ] 部署脚本执行成功 +- [ ] 前台页脚显示正确(ICP+统计) +- [ ] 详情页图标显示正确(Emoji) +- [ ] 后台Prompt管理菜单存在 +- [ ] 标签显示正常(有文字) +- [ ] AI生成功能测试通过 + +--- + +## 💡 优势说明 + +相比手动上传11个文件,Git patch方式: +- 只需上传2个文件(89KB) +- 自动处理文件合并 +- 保留完整Git历史 +- 可以一键回滚 +- 更安全可靠 diff --git a/MANUAL_DEPLOY.md b/MANUAL_DEPLOY.md new file mode 100644 index 0000000..4bfecf1 --- /dev/null +++ b/MANUAL_DEPLOY.md @@ -0,0 +1,195 @@ +# ZJPB v2.1 手动上传部署指南 + +## 📋 需要上传的文件(共10个) + +### 核心文件(必须上传) +1. ✅ `app.py` - 新增Prompt管理视图 +2. ✅ `models.py` - 新增PromptTemplate模型 +3. ✅ `migrate_prompts.py` - 数据库迁移脚本(新文件) + +### 模板文件(必须上传) +4. ✅ `templates/base_new.html` - 页脚和统计代码 +5. ✅ `templates/detail_new.html` - 图标修复 +6. ✅ `templates/admin/site/create.html` - 标签显示修复 +7. ✅ `templates/admin/site/edit.html` - 标签显示修复 + +### 辅助文件(可选) +8. ⭕ `INCREMENTAL_DEPLOY.md` - 部署文档 +9. ⭕ `export_data.py` - 数据导出工具 +10. ⭕ `migrate_db.py` - 数据库工具 + +### 部署脚本(新增) +11. ✅ `quick_deploy_server.sh` - 服务器端快速部署脚本 + +--- + +## 🚀 部署步骤(使用1Panel文件管理器) + +### 第一步:备份生产数据库 + +1. 登录1Panel管理面板 +2. 进入 **数据库** → 找到 `ai_nav` 数据库 +3. 点击 **备份** 按钮 +4. 下载备份文件到本地保存 + +### 第二步:备份现有代码 + +1. 在1Panel中进入 **文件管理** +2. 进入 `/www/wwwroot/` +3. 右键点击 `zjpb` 文件夹 +4. 选择 **压缩** → 命名为 `zjpb_backup_20250130.tar.gz` +5. 下载到本地保存 + +### 第三步:上传新文件 + +#### 方法1:使用1Panel文件管理器(推荐) + +1. 在1Panel文件管理器中进入 `/www/wwwroot/zjpb/` + +2. **上传核心文件**(覆盖): + - 上传 `app.py` 到 `/www/wwwroot/zjpb/` + - 上传 `models.py` 到 `/www/wwwroot/zjpb/` + - 上传 `migrate_prompts.py` 到 `/www/wwwroot/zjpb/`(新文件) + - 上传 `quick_deploy_server.sh` 到 `/www/wwwroot/zjpb/`(新文件) + +3. **上传模板文件**(覆盖): + - 进入 `/www/wwwroot/zjpb/templates/` + - 上传 `base_new.html` 到 `templates/` + - 上传 `detail_new.html` 到 `templates/` + - 进入 `templates/admin/site/` + - 上传 `create.html` 到 `templates/admin/site/` + - 上传 `edit.html` 到 `templates/admin/site/` + +#### 方法2:使用FTP工具(FileZilla/WinSCP) + +``` +服务器地址: your-server-ip +用户名: root +密码: your-password +端口: 22 (SFTP) +``` + +连接后进入 `/www/wwwroot/zjpb/`,直接拖拽文件上传覆盖。 + +### 第四步:设置脚本权限并执行部署 + +在1Panel终端或SSH中执行: + +```bash +# 进入项目目录 +cd /www/wwwroot/zjpb + +# 设置脚本执行权限 +chmod +x quick_deploy_server.sh + +# 执行部署脚本 +./quick_deploy_server.sh +``` + +**脚本会自动完成以下操作:** +1. ✅ 停止应用 +2. ✅ 备份当前代码 +3. ✅ 安装依赖(如有更新) +4. ✅ 运行数据库迁移 +5. ✅ 重启应用 +6. ✅ 检查状态 + +### 第五步:验证部署 + +**前台验证:** +1. 访问首页,检查页脚是否显示: + - `© 2025 ZJPB - 焦提示词 | AI工具导航` + - `浙ICP备2025154782号-1` + +2. 访问任意网站详情页,检查: + - 图标是否正常显示(不是Material Icons文本) + - 返回首页按钮显示 `←` 而不是 `arrow_back` + +**后台验证:** +1. 登录后台 `/admin/login` +2. 检查左侧菜单是否有 **Prompt管理** +3. 点击进入,应该看到3条默认数据: + - 标签生成 + - 主要功能生成 + - 详细介绍生成 + +4. 进入 **网站管理** → 编辑任意网站 +5. 检查标签区域是否正常显示标签名称(不是空白蓝框) + +**AI功能验证:** +1. 创建新网站或编辑现有网站 +2. 测试 "AI生成标签" 按钮 +3. 测试 "AI生成详细介绍" 按钮 +4. 测试 "AI生成主要功能" 按钮 + +--- + +## 🐛 常见问题 + +### Q1: 上传后文件权限错误 +```bash +# 设置正确的权限 +cd /www/wwwroot/zjpb +chmod 644 *.py +chmod 755 *.sh +``` + +### Q2: 脚本执行失败 +```bash +# 手动执行步骤 +cd /www/wwwroot/zjpb +./manage.sh stop +source venv/bin/activate +python migrate_prompts.py +./manage.sh start +``` + +### Q3: 数据库迁移报错 "表已存在" +这是正常的,说明之前已经运行过,可以忽略。 + +### Q4: 应用无法启动 +```bash +# 查看错误日志 +./manage.sh logs + +# 或 +tail -f logs/error.log +``` + +--- + +## 🔄 快速回滚(如出现问题) + +```bash +cd /www/wwwroot/zjpb +./manage.sh stop + +# 恢复备份 +cd /www/wwwroot +rm -rf zjpb +tar -xzf zjpb_backup_20250130.tar.gz + +# 重启 +cd zjpb +./manage.sh start +``` + +--- + +## 📊 部署检查清单 + +- [ ] 生产数据库已备份 +- [ ] 现有代码已备份 +- [ ] 所有文件上传完成 +- [ ] 部署脚本执行成功 +- [ ] 前台页脚显示正常 +- [ ] 详情页图标显示正常 +- [ ] 后台Prompt管理菜单存在 +- [ ] 标签显示正常 +- [ ] AI功能测试通过 + +--- + +## 💡 提示 + +如果你经常需要部署更新,建议配置Git远程仓库(GitHub/Gitee),下次就可以直接 `git pull` 更新,更加方便快捷。 diff --git a/QUICK_DEPLOY.md b/QUICK_DEPLOY.md new file mode 100644 index 0000000..8883708 --- /dev/null +++ b/QUICK_DEPLOY.md @@ -0,0 +1,94 @@ +# 1Panel部署快速指南 + +## 简化部署步骤 + +### 1. 准备工作(本地) + +1. 压缩项目: +```bash +# 排除不需要的文件 +zip -r zjpb.zip . -x "*.pyc" "*__pycache__*" "*.git*" ".env" "venv/*" "test_*.py" +``` + +### 2. 服务器操作 + +#### 2.1 上传并解压 +```bash +cd /www/wwwroot +mkdir zjpb +cd zjpb +# 上传zjpb.zip到此目录 +unzip zjpb.zip +``` + +#### 2.2 一键部署脚本 +```bash +# 赋予执行权限 +chmod +x deploy.sh + +# 执行部署 +./deploy.sh +``` + +### 3. 配置数据库 + +在1Panel中创建数据库: +- 名称:ai_nav +- 用户:ai_nav_user +- 密码:(记录下来) + +### 4. 配置环境变量 + +编辑 `.env` 文件: +```bash +nano .env +``` + +填写数据库信息和密钥。 + +### 5. 初始化 + +```bash +source venv/bin/activate +python init_db.py +``` + +### 6. 启动服务 + +**方法1: 使用管理脚本** +```bash +chmod +x manage.sh +./manage.sh start +``` + +**方法2: 使用1Panel** +在1Panel中创建反向代理网站,指向 `http://127.0.0.1:5000` + +### 7. 访问 + +- 前台:http://your-domain.com +- 后台:http://your-domain.com/admin/login + - 默认账号:admin / admin123 + +--- + +## 快速命令参考 + +```bash +# 启动应用 +./manage.sh start + +# 停止应用 +./manage.sh stop + +# 重启应用 +./manage.sh restart + +# 查看状态 +./manage.sh status + +# 查看日志 +./manage.sh logs +``` + +详细部署文档请查看:`DEPLOYMENT.md` diff --git a/UPLOAD_FILES_v2.2.0.txt b/UPLOAD_FILES_v2.2.0.txt new file mode 100644 index 0000000..2a781ec --- /dev/null +++ b/UPLOAD_FILES_v2.2.0.txt @@ -0,0 +1,221 @@ +================================================================================ +ZJPB v2.2.0 手动上传文件清单 +================================================================================ + +本地路径: D:\315mac\Code\zjpb\ +目标路径: /opt/1panel/apps/zjpb/ + +方案一:Git Patch方式(推荐) +-------------------------------- +只需上传2个文件: + +1. v2.2.0.patch (56KB) + 目标: /opt/1panel/apps/zjpb/v2.2.0.patch + +2. git_patch_deploy_v2.2.sh (2.6KB) + 目标: /opt/1panel/apps/zjpb/git_patch_deploy_v2.2.sh + +上传后执行: +cd /opt/1panel/apps/zjpb +chmod +x git_patch_deploy_v2.2.sh +./git_patch_deploy_v2.2.sh + +================================================================================ + +方案二:手动上传所有文件(如果不想用patch) +---------------------------------------- +需要上传9个文件: + +新增文件(4个): +-------------- +1. utils/news_searcher.py + 本地: D:\315mac\Code\zjpb\utils\news_searcher.py + 目标: /opt/1panel/apps/zjpb/utils/news_searcher.py + 说明: NewsSearcher工具类,封装博查API调用 + +2. migrate_news_fields.py + 本地: D:\315mac\Code\zjpb\migrate_news_fields.py + 目标: /opt/1panel/apps/zjpb/migrate_news_fields.py + 说明: 数据库迁移脚本,添加source_name和source_icon字段 + +3. test_news_feature.py + 本地: D:\315mac\Code\zjpb\test_news_feature.py + 目标: /opt/1panel/apps/zjpb/test_news_feature.py + 说明: 新闻功能测试脚本 + +4. fetch_news_cron.py + 本地: D:\315mac\Code\zjpb\fetch_news_cron.py + 目标: /opt/1panel/apps/zjpb/fetch_news_cron.py + 说明: 定期任务脚本,批量更新新闻 + +修改文件(4个): +-------------- +5. config.py + 本地: D:\315mac\Code\zjpb\config.py + 目标: /opt/1panel/apps/zjpb/config.py + 说明: 添加博查API配置 + +6. models.py + 本地: D:\315mac\Code\zjpb\models.py + 目标: /opt/1panel/apps/zjpb/models.py + 说明: News模型添加source_name和source_icon字段 + +7. app.py + 本地: D:\315mac\Code\zjpb\app.py + 目标: /opt/1panel/apps/zjpb/app.py + 说明: 添加新闻路由、智能更新逻辑、NewsAdmin优化 + +8. templates/detail_new.html + 本地: D:\315mac\Code\zjpb\templates\detail_new.html + 目标: /opt/1panel/apps/zjpb/templates/detail_new.html + 说明: 新闻展示UI,布局优化 + +文档文件(1个,可选): +-------------------- +9. NEWS_FEATURE_v2.2.md + 本地: D:\315mac\Code\zjpb\NEWS_FEATURE_v2.2.md + 目标: /opt/1panel/apps/zjpb/NEWS_FEATURE_v2.2.md + 说明: 新闻功能完整文档 + +================================================================================ + +手动上传后需要执行的操作: +------------------------ + +1. 运行数据库迁移: + cd /opt/1panel/apps/zjpb + source venv/bin/activate + python migrate_news_fields.py + +2. 配置.env文件,添加: + BOCHA_API_KEY=sk-76d0236a50d445ae92e75b634ed5313c + BOCHA_BASE_URL=https://api.bocha.cn + +3. 重启应用: + ./manage.sh restart + +4. 测试功能: + python test_news_feature.py + +================================================================================ + +文件详细信息: +------------ + +utils/news_searcher.py (271行) +- NewsSearcher类 +- 博查API封装 +- 新闻搜索和解析 +- 错误处理 + +migrate_news_fields.py (99行) +- 添加source_name字段 +- 添加source_icon字段 +- 检查字段是否已存在 +- 显示表结构 + +test_news_feature.py (142行) +- API配置检查 +- 数据库连接测试 +- 新闻搜索测试 +- 保存数据测试 + +fetch_news_cron.py (167行) +- 批量新闻更新 +- 命令行参数支持 +- 进度显示 +- 错误处理 + +config.py (修改部分) +- BOCHA_API_KEY配置 +- BOCHA_BASE_URL配置 +- BOCHA_SEARCH_ENDPOINT配置 +- NEWS_SEARCH_*配置 + +models.py (修改部分) +- News.source_name字段 +- News.source_icon字段 + +app.py (修改部分) +- site_detail路由:智能新闻更新逻辑(64行新增) +- /api/fetch-site-news路由(91行) +- /api/fetch-all-news路由(105行) +- NewsAdmin: 新增source_name和source_icon字段 + +templates/detail_new.html (修改部分) +- 新闻模块HTML(47行) +- 新闻来源展示 +- 布局调整(新闻左侧,推荐右侧) + +================================================================================ + +推荐使用方案一(Git Patch)的原因: +--------------------------------- +✅ 只需上传2个文件(58.6KB总共) +✅ 自动处理文件合并,避免手动覆盖错误 +✅ Git历史完整,可追溯 +✅ 自动备份现有修改 +✅ 一键回滚 +✅ 自动运行数据库迁移 +✅ 自动重启应用 + +手动上传的缺点: +-------------- +❌ 需要上传9个文件 +❌ 需要确保文件路径正确 +❌ 需要手动运行迁移脚本 +❌ 需要手动重启应用 +❌ 无法自动回滚 +❌ 可能覆盖生产环境的修改 + +================================================================================ + +1Panel文件管理器上传步骤: +------------------------- + +1. 登录1Panel管理面板 +2. 进入"文件"菜单 +3. 导航到 /opt/1panel/apps/zjpb/ +4. 点击"上传"按钮 +5. 选择需要上传的文件 +6. 等待上传完成 + +如果上传utils/news_searcher.py: +- 先导航到 /opt/1panel/apps/zjpb/utils/ +- 然后上传 news_searcher.py + +如果上传templates/detail_new.html: +- 先导航到 /opt/1panel/apps/zjpb/templates/ +- 然后上传 detail_new.html + +================================================================================ + +SCP命令上传(如果使用SSH): +-------------------------- + +# 方案一:上传patch文件 +scp v2.2.0.patch root@your-server:/opt/1panel/apps/zjpb/ +scp git_patch_deploy_v2.2.sh root@your-server:/opt/1panel/apps/zjpb/ + +# 方案二:上传所有文件 +scp utils/news_searcher.py root@your-server:/opt/1panel/apps/zjpb/utils/ +scp migrate_news_fields.py root@your-server:/opt/1panel/apps/zjpb/ +scp test_news_feature.py root@your-server:/opt/1panel/apps/zjpb/ +scp fetch_news_cron.py root@your-server:/opt/1panel/apps/zjpb/ +scp config.py root@your-server:/opt/1panel/apps/zjpb/ +scp models.py root@your-server:/opt/1panel/apps/zjpb/ +scp app.py root@your-server:/opt/1panel/apps/zjpb/ +scp templates/detail_new.html root@your-server:/opt/1panel/apps/zjpb/templates/ +scp NEWS_FEATURE_v2.2.md root@your-server:/opt/1panel/apps/zjpb/ + +================================================================================ + +建议: +----- +强烈推荐使用方案一(Git Patch)! +- 更安全 +- 更快速 +- 更可靠 +- 更易回滚 + +================================================================================ diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..34b223e --- /dev/null +++ b/deploy.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +# ZJPB 一键部署脚本 +# 用于1Panel环境快速部署 + +echo "================================" +echo "ZJPB - 焦提示词 部署脚本" +echo "================================" +echo "" + +# 检查Python版本 +python_version=$(python3 --version 2>&1 | awk '{print $2}') +echo "✓ Python版本: $python_version" + +# 创建必要目录 +echo "创建必要目录..." +mkdir -p logs +mkdir -p static/uploads +echo "✓ 目录创建完成" + +# 创建虚拟环境 +if [ ! -d "venv" ]; then + echo "创建Python虚拟环境..." + python3 -m venv venv + echo "✓ 虚拟环境创建完成" +else + echo "✓ 虚拟环境已存在" +fi + +# 激活虚拟环境 +source venv/bin/activate + +# 升级pip +echo "升级pip..." +pip install --upgrade pip -q + +# 安装依赖 +echo "安装Python依赖包..." +pip install -r requirements.txt -q +echo "✓ 依赖包安装完成" + +# 检查.env文件 +if [ ! -f ".env" ]; then + echo "" + echo "⚠️ 警告: .env文件不存在" + echo "请从.env.example复制并配置:" + echo " cp .env.example .env" + echo " nano .env" + echo "" + read -p "是否现在创建.env文件? (y/n) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + cp .env.example .env + echo "✓ .env文件已创建,请编辑配置" + nano .env + fi +else + echo "✓ .env配置文件存在" +fi + +# 询问是否初始化数据库 +echo "" +read -p "是否需要初始化数据库? (首次部署选择y) (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "初始化数据库..." + python init_db.py + echo "✓ 数据库初始化完成" + echo "" + echo "默认管理员账号:" + echo " 用户名: admin" + echo " 密码: admin123" + echo " ⚠️ 请登录后立即修改密码!" +fi + +# 设置权限 +echo "" +echo "设置目录权限..." +chmod 755 logs static/uploads +chmod +x manage.sh +echo "✓ 权限设置完成" + +echo "" +echo "================================" +echo "部署完成!" +echo "================================" +echo "" +echo "后续步骤:" +echo "1. 确保已在1Panel中创建MySQL数据库" +echo "2. 确保.env文件配置正确" +echo "3. 在1Panel中创建反向代理网站,指向: http://127.0.0.1:5000" +echo "4. 启动应用: ./manage.sh start" +echo "5. 访问网站并修改默认密码" +echo "" +echo "详细文档: 查看 DEPLOYMENT.md" +echo "" diff --git a/export_sites.py b/export_sites.py new file mode 100644 index 0000000..366da09 --- /dev/null +++ b/export_sites.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +""" +导出网站数据 +生成CSV文件,方便批量导入 +""" +import csv +import sys +from app import create_app +from models import Site, Tag + +def export_sites_to_csv(): + """导出网站数据到CSV""" + app = create_app('development') + + with app.app_context(): + sites = Site.query.order_by(Site.sort_order.desc()).all() + + if not sites: + print("没有找到任何网站数据") + return + + # 导出为CSV + filename = 'sites_export.csv' + with open(filename, 'w', newline='', encoding='utf-8-sig') as f: + writer = csv.writer(f) + + # 写入表头 + writer.writerow([ + '网站名称', + 'URL', + '简短描述', + '详细描述', + '特色功能', + '标签(逗号分隔)', + '排序值', + '是否可见' + ]) + + # 写入数据 + for site in sites: + tags_str = ', '.join([tag.name for tag in site.tags]) + writer.writerow([ + site.name, + site.url, + site.short_desc or '', + site.description or '', + site.features or '', + tags_str, + site.sort_order, + '是' if site.is_visible else '否' + ]) + + print(f"✓ 成功导出 {len(sites)} 个网站") + print(f" 文件: {filename}") + print("\n网站列表:") + print("-" * 80) + + for i, site in enumerate(sites, 1): + tags = ', '.join([tag.name for tag in site.tags]) + print(f"{i}. {site.name}") + print(f" URL: {site.url}") + print(f" 描述: {site.short_desc}") + print(f" 标签: {tags}") + print() + +if __name__ == '__main__': + export_sites_to_csv() diff --git a/export_urls.py b/export_urls.py new file mode 100644 index 0000000..997ded7 --- /dev/null +++ b/export_urls.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +""" +导出网站URL列表 +""" +from app import create_app +from models import Site + +def export_urls(): + """导出所有网站URL""" + app = create_app('development') + + with app.app_context(): + sites = Site.query.order_by(Site.sort_order.desc()).all() + + if not sites: + print("没有找到任何网站数据") + return + + # 导出到文件 + filename = 'urls.txt' + with open(filename, 'w', encoding='utf-8') as f: + for site in sites: + f.write(site.url + '\n') + + print(f"✓ 成功导出 {len(sites)} 个URL") + print(f" 文件: {filename}") + print("\nURL列表:") + print("-" * 80) + for url in [site.url for site in sites]: + print(url) + +if __name__ == '__main__': + export_urls() diff --git a/git_patch_deploy.sh b/git_patch_deploy.sh new file mode 100644 index 0000000..60abcf9 --- /dev/null +++ b/git_patch_deploy.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# ZJPB v2.1 Git Patch 部署脚本 +# 在生产服务器上执行 + +echo "================================" +echo "ZJPB v2.1 Git Patch 部署" +echo "================================" +echo "" + +# 项目路径 +PROJECT_DIR="/opt/1panel/apps/zjpb" + +# 检查是否在正确目录 +cd $PROJECT_DIR || { echo "❌ 项目目录不存在"; exit 1; } + +echo "当前目录: $(pwd)" +echo "" + +# 停止应用 +echo "1. 停止应用..." +./manage.sh stop +sleep 2 + +# 检查Git状态 +echo "2. 检查Git状态..." +git status + +# 备份未提交的修改(如果有) +echo "3. 备份当前修改(如有)..." +if ! git diff-index --quiet HEAD --; then + echo " 发现未提交的修改,正在保存..." + git stash save "backup_before_v2.1_$(date +%Y%m%d_%H%M%S)" +fi + +# 应用patch +echo "4. 应用v2.1.0补丁..." +if [ -f "v2.1.0.patch" ]; then + git apply --check v2.1.0.patch + if [ $? -eq 0 ]; then + git apply v2.1.0.patch + echo " ✅ 补丁应用成功" + else + echo " ❌ 补丁应用失败,请检查" + exit 1 + fi +else + echo " ❌ v2.1.0.patch 文件不存在" + exit 1 +fi + +# 提交更改 +echo "5. 提交更改到Git..." +git add . +git commit -m "release: v2.1.0 - Prompt管理系统、页脚优化、图标修复 + +通过patch部署,包含以下更新: +- Prompt管理系统 +- 页脚ICP备案和统计代码 +- 详情页图标修复 +- 标签显示修复 +" + +# 激活虚拟环境 +echo "6. 激活虚拟环境..." +source venv/bin/activate + +# 安装依赖 +echo "7. 检查依赖..." +pip install -r requirements.txt -q + +# 运行数据库迁移 +echo "8. 运行数据库迁移..." +python migrate_prompts.py + +# 重启应用 +echo "9. 重启应用..." +./manage.sh start +sleep 3 + +# 检查状态 +echo "10. 检查应用状态..." +./manage.sh status + +echo "" +echo "================================" +echo "✅ 部署完成!" +echo "================================" +echo "" +echo "Git提交历史:" +git log --oneline -3 +echo "" +echo "请访问网站验证更新是否成功" +echo "" diff --git a/gunicorn_config.py b/gunicorn_config.py new file mode 100644 index 0000000..87e0129 --- /dev/null +++ b/gunicorn_config.py @@ -0,0 +1,44 @@ +# Gunicorn配置文件 +import os + +# 绑定地址和端口 +bind = "0.0.0.0:5000" + +# 工作进程数 (推荐: CPU核心数 * 2 + 1) +workers = 4 + +# 工作线程数 +threads = 2 + +# 工作模式 +worker_class = "sync" + +# 超时时间(秒) +timeout = 120 + +# 访问日志 +accesslog = "logs/access.log" + +# 错误日志 +errorlog = "logs/error.log" + +# 日志级别 +loglevel = "info" + +# 进程名称 +proc_name = "zjpb_app" + +# 守护进程模式(设置为False,由systemd或supervisor管理) +daemon = False + +# PID文件 +pidfile = "logs/gunicorn.pid" + +# 预加载应用(提高性能) +preload_app = True + +# 优雅重启超时 +graceful_timeout = 30 + +# 保持连接 +keepalive = 5 diff --git a/manage.sh b/manage.sh new file mode 100644 index 0000000..51cf1e9 --- /dev/null +++ b/manage.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# ZJPB 应用管理脚本 +# 用法: ./manage.sh [start|stop|restart|status|logs] + +APP_NAME="zjpb" +APP_DIR="/www/wwwroot/zjpb" +VENV_DIR="$APP_DIR/venv" +PID_FILE="$APP_DIR/logs/gunicorn.pid" + +cd $APP_DIR + +case "$1" in + start) + echo "启动 $APP_NAME..." + source $VENV_DIR/bin/activate + gunicorn -c gunicorn_config.py app:app + echo "$APP_NAME 已启动" + ;; + + stop) + echo "停止 $APP_NAME..." + if [ -f $PID_FILE ]; then + kill $(cat $PID_FILE) + echo "$APP_NAME 已停止" + else + echo "PID文件不存在,可能未运行" + fi + ;; + + restart) + $0 stop + sleep 2 + $0 start + ;; + + status) + if [ -f $PID_FILE ]; then + PID=$(cat $PID_FILE) + if ps -p $PID > /dev/null; then + echo "$APP_NAME 正在运行 (PID: $PID)" + else + echo "$APP_NAME 未运行(但PID文件存在)" + fi + else + echo "$APP_NAME 未运行" + fi + ;; + + logs) + echo "实时查看日志(Ctrl+C退出):" + tail -f logs/error.log + ;; + + *) + echo "用法: $0 {start|stop|restart|status|logs}" + exit 1 + ;; +esac + +exit 0 diff --git a/one_click_deploy.sh b/one_click_deploy.sh new file mode 100644 index 0000000..8da3098 --- /dev/null +++ b/one_click_deploy.sh @@ -0,0 +1,121 @@ +#!/bin/bash +# ZJPB v2.1 一键部署命令集合 +# 复制整个脚本到服务器终端执行即可 + +set -e # 遇到错误立即停止 + +echo "================================" +echo "ZJPB v2.1 一键部署" +echo "================================" +echo "" + +# 进入项目目录 +cd /opt/1panel/apps/zjpb + +# 显示当前位置 +echo "📂 当前目录: $(pwd)" +echo "" + +# 停止应用 +echo "⏸️ 停止应用..." +./manage.sh stop 2>/dev/null || echo "应用未运行" +sleep 2 + +# 检查Git状态 +echo "" +echo "🔍 检查Git状态..." +git status --short + +# 备份未提交的修改 +echo "" +echo "💾 备份当前修改..." +if ! git diff-index --quiet HEAD -- 2>/dev/null; then + git stash save "backup_before_v2.1_$(date +%Y%m%d_%H%M%S)" + echo " ✅ 已保存未提交的修改" +else + echo " ℹ️ 无需备份" +fi + +# 检查patch文件 +echo "" +echo "📦 检查补丁文件..." +if [ ! -f "v2.1.0.patch" ]; then + echo " ❌ v2.1.0.patch 不存在,请先上传" + exit 1 +fi +echo " ✅ v2.1.0.patch 存在" + +# 应用patch +echo "" +echo "🔧 应用v2.1.0补丁..." +git apply --check v2.1.0.patch 2>&1 +if [ $? -eq 0 ]; then + git apply v2.1.0.patch + echo " ✅ 补丁应用成功" +else + echo " ❌ 补丁应用失败" + exit 1 +fi + +# 提交更改 +echo "" +echo "📝 提交更改到Git..." +git add . +git commit -m "release: v2.1.0 - Prompt管理系统、页脚优化、图标修复 + +部署时间: $(date '+%Y-%m-%d %H:%M:%S') +部署方式: Git Patch + +更新内容: +- 新增Prompt管理系统 +- 页脚添加ICP备案号和统计代码 +- 详情页图标优化(Material Icons → Emoji) +- 修复标签显示问题 +" 2>&1 + +# 激活虚拟环境 +echo "" +echo "🐍 激活虚拟环境..." +source venv/bin/activate + +# 安装依赖 +echo "" +echo "📚 检查依赖..." +pip install -r requirements.txt -q + +# 运行数据库迁移 +echo "" +echo "🗄️ 运行数据库迁移..." +python migrate_prompts.py + +# 重启应用 +echo "" +echo "🚀 重启应用..." +./manage.sh start +sleep 3 + +# 检查状态 +echo "" +echo "✅ 检查应用状态..." +./manage.sh status + +# 显示Git历史 +echo "" +echo "📜 Git提交历史(最近3条):" +git log --oneline -3 + +echo "" +echo "================================" +echo "🎉 部署完成!" +echo "================================" +echo "" +echo "📋 验证清单:" +echo " 1. 访问首页,检查页脚ICP备案号" +echo " 2. 访问详情页,检查图标是否为emoji" +echo " 3. 登录后台,检查Prompt管理菜单" +echo " 4. 编辑网站,检查标签显示是否正常" +echo " 5. 测试AI生成功能" +echo "" +echo "🔧 查看日志: ./manage.sh logs" +echo "📊 查看状态: ./manage.sh status" +echo "" diff --git a/quick_deploy_server.sh b/quick_deploy_server.sh new file mode 100644 index 0000000..7651792 --- /dev/null +++ b/quick_deploy_server.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# ZJPB v2.1 快速部署脚本 +# 在生产服务器上执行此脚本 + +echo "================================" +echo "ZJPB v2.1 增量部署" +echo "================================" +echo "" + +# 检查是否在正确目录 +if [ ! -f "app.py" ]; then + echo "❌ 错误:请在项目根目录执行此脚本" + exit 1 +fi + +# 停止应用 +echo "1. 停止应用..." +./manage.sh stop +sleep 2 + +# 备份当前代码 +echo "2. 备份当前代码..." +BACKUP_DIR="../zjpb_backup_$(date +%Y%m%d_%H%M%S)" +cp -r ../zjpb "$BACKUP_DIR" +echo " 备份已保存到: $BACKUP_DIR" + +# 激活虚拟环境 +echo "3. 激活虚拟环境..." +source venv/bin/activate + +# 安装依赖(如有更新) +echo "4. 检查依赖..." +pip install -r requirements.txt -q + +# 运行数据库迁移 +echo "5. 运行数据库迁移..." +python migrate_prompts.py + +# 重启应用 +echo "6. 重启应用..." +./manage.sh start +sleep 2 + +# 检查状态 +echo "7. 检查应用状态..." +./manage.sh status + +echo "" +echo "================================" +echo "✅ 部署完成!" +echo "================================" +echo "" +echo "验证项目:" +echo "1. 访问前台首页,检查页脚ICP备案号" +echo "2. 访问详情页,检查图标是否正常" +echo "3. 登录后台,检查Prompt管理菜单" +echo "4. 编辑网站,检查标签是否正常显示" +echo "" +echo "如遇问题,可快速回滚:" +echo " ./manage.sh stop" +echo " cd .." +echo " rm -rf zjpb" +echo " mv $BACKUP_DIR zjpb" +echo " cd zjpb" +echo " ./manage.sh start" +echo "" diff --git a/static/logos/logo_5fda15d305a84866.jpg b/static/logos/logo_5fda15d305a84866.jpg new file mode 100644 index 0000000..ff90370 Binary files /dev/null and b/static/logos/logo_5fda15d305a84866.jpg differ diff --git a/static/logos/logo_aipmclub_com.png b/static/logos/logo_aipmclub_com.png new file mode 100644 index 0000000..0c34f2b Binary files /dev/null and b/static/logos/logo_aipmclub_com.png differ diff --git a/templates/admin/change_password.html b/templates/admin/change_password.html new file mode 100644 index 0000000..e8e8f89 --- /dev/null +++ b/templates/admin/change_password.html @@ -0,0 +1,370 @@ + + + + + + 修改密码 - ZJPB 焦提示词 + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ 控制台 + / + 修改密码 +
+
+ + + +
+
+ + +
+ + + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + + {% endfor %} + {% endif %} + {% endwith %} + +
+
+
+
+
+ +
+ +
+
+ + lock + +
+ +
+
+ + +
+ +
+
+ + lock_reset + +
+ +
+ 密码长度至少6位 +
+ + +
+ +
+
+ + check_circle + +
+ +
+
+ + +
+ + + cancel + 取消 + +
+
+
+
+ + +
+ info + 安全提示: +
    +
  • 密码修改成功后,您将被自动登出,需要使用新密码重新登录
  • +
  • 请妥善保管您的密码,不要与他人分享
  • +
  • 建议定期修改密码以保证账号安全
  • +
+
+
+
+
+
+ + + + + + + + + + diff --git a/test_deepseek.py b/test_deepseek.py new file mode 100644 index 0000000..127b642 --- /dev/null +++ b/test_deepseek.py @@ -0,0 +1,52 @@ +"""测试DeepSeek API配置""" +import os +from dotenv import load_dotenv +from openai import OpenAI + +# 加载环境变量 +load_dotenv() + +def test_deepseek_api(): + """测试DeepSeek API连接""" + api_key = os.getenv('DEEPSEEK_API_KEY') + base_url = os.getenv('DEEPSEEK_BASE_URL', 'https://api.deepseek.com') + + print(f"API Key: {api_key[:20]}..." if api_key else "未找到API Key") + print(f"Base URL: {base_url}") + + if not api_key: + print("[ERROR] DEEPSEEK_API_KEY not configured") + return False + + try: + # 创建客户端 + client = OpenAI( + api_key=api_key, + base_url=base_url + ) + + # 发送测试请求 + print("\nTesting API connection...") + response = client.chat.completions.create( + model="deepseek-chat", + messages=[ + {"role": "system", "content": "你是一个AI助手"}, + {"role": "user", "content": "你好,请用一句话介绍你自己"} + ], + max_tokens=100 + ) + + result = response.choices[0].message.content + # 移除emoji和特殊字符 + result_clean = result.encode('ascii', 'ignore').decode('ascii') + print(f"\n[SUCCESS] API connection successful!") + print(f"Response: {result_clean if result_clean else result[:50]}") + print(f"Usage: {response.usage}") + return True + + except Exception as e: + print(f"\n[ERROR] API connection failed: {str(e)}") + return False + +if __name__ == '__main__': + test_deepseek_api() diff --git a/v2.1.0.patch b/v2.1.0.patch new file mode 100644 index 0000000..26bf5ca --- /dev/null +++ b/v2.1.0.patch @@ -0,0 +1,2438 @@ +From 30b1ef75d655f962da385ad8630e06562a8b5bdf Mon Sep 17 00:00:00 2001 +From: Jowe <123822645+Selei1983@users.noreply.github.com> +Date: Tue, 30 Dec 2025 00:45:39 +0800 +Subject: [PATCH] =?UTF-8?q?release:=20v2.1=20-=20Prompt=E7=AE=A1=E7=90=86?= + =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E3=80=81=E9=A1=B5=E8=84=9A=E4=BC=98=E5=8C=96?= + =?UTF-8?q?=E3=80=81=E5=9B=BE=E6=A0=87=E4=BF=AE=E5=A4=8D?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +新增功能: +- Prompt管理:后台新增Prompt模板管理功能 +- 数据库迁移:新增prompt_templates表及默认数据 +- 页脚优化:添加ICP备案号(浙ICP备2025154782号-1)和Microsoft Clarity统计 +- 图标修复:详情页Material Icons替换为Emoji +- 标签显示:修复编辑页标签名称无法显示的问题 + +技术改进: +- 添加正则表达式提取标签名称 +- 优化页脚样式和链接 +- 完善增量部署文档 + +🚀 Generated with Claude Code +Co-Authored-By: Claude Sonnet 4.5 +--- + INCREMENTAL_DEPLOY.md | 208 ++++++++++++ + app.py | 313 +++++++++++++++++- + export_data.py | 58 ++++ + migrate_db.py | 49 +++ + migrate_prompts.py | 141 +++++++++ + models.py | 32 ++ + templates/admin/site/create.html | 455 ++++++++++++++++++++++++++- + templates/admin/site/edit.html | 522 +++++++++++++++++++++++++++++-- + templates/base_new.html | 40 ++- + templates/detail_new.html | 165 ++++++++-- + 10 files changed, 1887 insertions(+), 96 deletions(-) + create mode 100644 INCREMENTAL_DEPLOY.md + create mode 100644 export_data.py + create mode 100644 migrate_db.py + create mode 100644 migrate_prompts.py + +diff --git a/INCREMENTAL_DEPLOY.md b/INCREMENTAL_DEPLOY.md +new file mode 100644 +index 0000000..dc18d04 +--- /dev/null ++++ b/INCREMENTAL_DEPLOY.md +@@ -0,0 +1,208 @@ ++# ZJPB v2.1 增量部署指南 ++ ++## 📋 本次更新内容 ++ ++### 新增功能 ++1. **Prompt管理系统** - 后台可管理AI提示词模板 ++2. **详情页图标优化** - Material Icons替换为Emoji ++3. **标签显示修复** - 修复编辑页标签名称无法显示问题 ++4. **页脚优化** - 添加ICP备案号和Microsoft Clarity统计 ++ ++### 数据库变更 ++- 新增表:`prompt_templates` (Prompt模板表) ++- 无现有表结构变更,完全兼容旧数据 ++ ++--- ++ ++## 🔒 增量部署步骤 ++ ++### 第一步:备份生产数据库(必须!) ++ ++在1Panel中备份数据库: ++```bash ++# 方法1:使用1Panel面板 ++1. 进入1Panel -> 数据库 ++2. 找到 ai_nav 数据库 ++3. 点击"备份"按钮 ++4. 下载备份文件到本地保存 ++ ++# 方法2:使用命令行 ++mysqldump -h 112.124.42.38 -u ai_nav_user -p ai_nav > backup_$(date +%Y%m%d_%H%M%S).sql ++``` ++ ++### 第二步:停止生产应用 ++ ++```bash ++# SSH登录到生产服务器 ++ssh root@your-server-ip ++ ++# 进入应用目录 ++cd /www/wwwroot/zjpb ++ ++# 停止应用 ++./manage.sh stop ++``` ++ ++### 第三步:备份现有代码 ++ ++```bash ++# 创建备份目录 ++cd /www/wwwroot ++cp -r zjpb zjpb_backup_$(date +%Y%m%d_%H%M%S) ++``` ++ ++### 第四步:上传新代码 ++ ++**方法1:使用Git(推荐)** ++ ++```bash ++# 在本地提交所有修改 ++git add . ++git commit -m "release: v2.1 - Prompt管理系统、页脚优化、图标修复" ++git push origin master ++ ++# 在服务器上拉取 ++cd /www/wwwroot/zjpb ++git pull origin master ++``` ++ ++**方法2:手动上传** ++ ++```bash ++# 在本地压缩(排除不需要的文件) ++zip -r zjpb_v2.1.zip . -x "*.pyc" "*__pycache__*" "*.git*" ".env" "venv/*" "test_*.py" "*.db" "nul" ++ ++# 上传到服务器 /www/wwwroot/zjpb_new.zip ++# 然后解压覆盖 ++cd /www/wwwroot/zjpb ++unzip -o ../zjpb_new.zip ++``` ++ ++### 第五步:安装新依赖(如有) ++ ++```bash ++cd /www/wwwroot/zjpb ++source venv/bin/activate ++pip install -r requirements.txt ++``` ++ ++### 第六步:运行数据库迁移 ++ ++```bash ++# 激活虚拟环境 ++source venv/bin/activate ++ ++# 运行迁移脚本(创建 prompt_templates 表) ++python migrate_prompts.py ++``` ++ ++**预期输出:** ++``` ++正在创建 prompt_templates 表... ++[OK] 表创建成功 ++正在初始化默认prompt模板... ++[OK] 默认prompt模板初始化成功 ++ - 标签生成: 1 ++ - 主要功能生成: 2 ++ - 详细介绍生成: 3 ++``` ++ ++### 第七步:重启应用 ++ ++```bash ++./manage.sh start ++``` ++ ++### 第八步:验证部署 ++ ++**检查项:** ++ ++1. **访问前台首页** ++ - 检查页脚是否显示ICP备案号 ++ - 检查Clarity统计是否加载(F12查看Network) ++ ++2. **访问详情页** ++ - 检查图标是否正常显示(不是Material Icons文本) ++ ++3. **登录后台** ++ - 检查是否有"Prompt管理"菜单 ++ - 进入Prompt管理,查看是否有3条默认数据 ++ ++4. **测试网站编辑** ++ - 编辑任意网站,检查标签是否正常显示(不是空白蓝框) ++ ++5. **测试AI功能** ++ - 创建新网站,测试"AI生成标签"功能 ++ - 测试"AI生成详细介绍"功能 ++ - 测试"AI生成主要功能"功能 ++ ++--- ++ ++## 🔄 回滚方案(如出现问题) ++ ++### 快速回滚代码 ++ ++```bash ++cd /www/wwwroot ++./zjpb/manage.sh stop ++ ++# 删除新版本 ++rm -rf zjpb ++ ++# 恢复备份 ++mv zjpb_backup_YYYYMMDD_HHMMSS zjpb ++ ++# 重启 ++cd zjpb ++./manage.sh start ++``` ++ ++### 恢复数据库 ++ ++```bash ++# 如果新表导致问题,可以删除新表 ++mysql -h 112.124.42.38 -u ai_nav_user -p ai_nav ++ ++# 在MySQL中执行 ++DROP TABLE IF EXISTS prompt_templates; ++``` ++ ++--- ++ ++## 📝 注意事项 ++ ++1. ✅ **本次更新不会影响现有数据** - 只是新增表,不修改现有表 ++2. ✅ **完全向后兼容** - 即使不运行迁移脚本,前台也能正常访问 ++3. ✅ **可以随时回滚** - 保留了完整备份 ++4. ⚠️ **必须备份数据库** - 虽然风险很低,但备份是必须的 ++5. ⚠️ **检查.env配置** - 确保生产环境的.env配置正确 ++ ++--- ++ ++## 🐛 常见问题 ++ ++### Q1: 迁移脚本报错 "表已存在" ++**A:** 说明之前已经运行过迁移,可以跳过此步骤 ++ ++### Q2: Prompt管理菜单看不到 ++**A:** 清除浏览器缓存,重新登录后台 ++ ++### Q3: 标签还是显示不出来 ++**A:** 清除浏览器缓存,强制刷新(Ctrl+F5) ++ ++### Q4: Clarity统计没加载 ++**A:** 检查网络是否能访问 clarity.ms,可能被墙 ++ ++--- ++ ++## 📞 技术支持 ++ ++如遇问题,请检查日志: ++ ++```bash ++# 查看应用日志 ++./manage.sh logs ++ ++# 查看错误日志 ++tail -f logs/error.log ++``` +diff --git a/app.py b/app.py +index 14288f1..7a2e392 100644 +--- a/app.py ++++ b/app.py +@@ -1,11 +1,12 @@ + import os ++import markdown + from flask import Flask, render_template, redirect, url_for, request, flash, jsonify + from flask_login import LoginManager, login_user, logout_user, login_required, current_user + from flask_admin import Admin, AdminIndexView, expose + from flask_admin.contrib.sqla import ModelView + from datetime import datetime + from config import config +-from models import db, Site, Tag, Admin as AdminModel, News ++from models import db, Site, Tag, Admin as AdminModel, News, site_tags, PromptTemplate + from utils.website_fetcher import WebsiteFetcher + from utils.tag_generator import TagGenerator + +@@ -19,6 +20,14 @@ def create_app(config_name='default'): + # 初始化数据库 + db.init_app(app) + ++ # 添加Markdown过滤器 ++ @app.template_filter('markdown') ++ def markdown_filter(text): ++ """将Markdown文本转换为HTML""" ++ if not text: ++ return '' ++ return markdown.markdown(text, extensions=['nl2br', 'fenced_code']) ++ + # 初始化登录管理 + login_manager = LoginManager() + login_manager.init_app(app) +@@ -36,6 +45,22 @@ def create_app(config_name='default'): + # 获取所有启用的标签 + tags = Tag.query.order_by(Tag.sort_order.desc(), Tag.id).all() + ++ # 优化:使用一次SQL查询统计所有标签的网站数量 ++ tag_counts = {} ++ if tags: ++ # 使用JOIN查询一次性获取所有标签的网站数量 ++ from sqlalchemy import func ++ counts_query = db.session.query( ++ site_tags.c.tag_id, ++ func.count(site_tags.c.site_id).label('count') ++ ).join( ++ Site, site_tags.c.site_id == Site.id ++ ).filter( ++ Site.is_active == True ++ ).group_by(site_tags.c.tag_id).all() ++ ++ tag_counts = {tag_id: count for tag_id, count in counts_query} ++ + # 获取筛选参数 + tag_slug = request.args.get('tag') + search_query = request.args.get('q', '').strip() +@@ -57,7 +82,7 @@ def create_app(config_name='default'): + pagination = None + return render_template('index_new.html', sites=sites, tags=tags, + selected_tag=selected_tag, search_query=search_query, +- pagination=pagination) ++ pagination=pagination, tag_counts=tag_counts) + + # 搜索功能 + if search_query: +@@ -79,7 +104,7 @@ def create_app(config_name='default'): + + return render_template('index_new.html', sites=sites, tags=tags, + selected_tag=selected_tag, search_query=search_query, +- pagination=pagination) ++ pagination=pagination, tag_counts=tag_counts) + + @app.route('/site/') + def site_detail(code): +@@ -138,6 +163,51 @@ def create_app(config_name='default'): + logout_user() + return redirect(url_for('index')) + ++ @app.route('/admin/change-password', methods=['GET', 'POST']) ++ @login_required ++ def change_password(): ++ """修改密码""" ++ if request.method == 'POST': ++ old_password = request.form.get('old_password', '').strip() ++ new_password = request.form.get('new_password', '').strip() ++ confirm_password = request.form.get('confirm_password', '').strip() ++ ++ # 验证旧密码 ++ if not current_user.check_password(old_password): ++ flash('旧密码错误', 'error') ++ return render_template('admin/change_password.html') ++ ++ # 验证新密码 ++ if not new_password: ++ flash('新密码不能为空', 'error') ++ return render_template('admin/change_password.html') ++ ++ if len(new_password) < 6: ++ flash('新密码长度至少6位', 'error') ++ return render_template('admin/change_password.html') ++ ++ if new_password != confirm_password: ++ flash('两次输入的新密码不一致', 'error') ++ return render_template('admin/change_password.html') ++ ++ if old_password == new_password: ++ flash('新密码不能与旧密码相同', 'error') ++ return render_template('admin/change_password.html') ++ ++ # 修改密码 ++ try: ++ current_user.set_password(new_password) ++ db.session.commit() ++ flash('密码修改成功,请重新登录', 'success') ++ logout_user() ++ return redirect(url_for('admin_login')) ++ except Exception as e: ++ db.session.rollback() ++ flash(f'密码修改失败:{str(e)}', 'error') ++ return render_template('admin/change_password.html') ++ ++ return render_template('admin/change_password.html') ++ + # ========== API路由 ========== + @app.route('/api/fetch-website-info', methods=['POST']) + @login_required +@@ -165,17 +235,18 @@ def create_app(config_name='default'): + 'message': '无法获取网站信息,请检查URL是否正确或手动填写' + }) + +- # 下载Logo(如果有) ++ # 下载Logo到本地(如果有) + logo_path = None + if info.get('logo_url'): +- logo_path = fetcher.download_logo(info['logo_url']) ++ logo_path = fetcher.download_logo(info['logo_url'], save_dir='static/logos') + ++ # 如果下载失败,不返回远程URL,让用户手动上传 + return jsonify({ + 'success': True, + 'data': { + 'name': info.get('name', ''), + 'description': info.get('description', ''), +- 'logo': logo_path or info.get('logo_url', '') ++ 'logo': logo_path if logo_path else '' + } + }) + +@@ -185,6 +256,148 @@ def create_app(config_name='default'): + 'message': f'抓取失败: {str(e)}' + }), 500 + ++ @app.route('/api/upload-logo', methods=['POST']) ++ @login_required ++ def upload_logo(): ++ """上传Logo图片API""" ++ try: ++ # 检查文件是否存在 ++ if 'logo' not in request.files: ++ return jsonify({ ++ 'success': False, ++ 'message': '请选择要上传的图片' ++ }), 400 ++ ++ file = request.files['logo'] ++ ++ # 检查文件名 ++ if file.filename == '': ++ return jsonify({ ++ 'success': False, ++ 'message': '未选择文件' ++ }), 400 ++ ++ # 检查文件类型 ++ allowed_extensions = {'png', 'jpg', 'jpeg', 'gif', 'svg', 'ico', 'webp'} ++ filename = file.filename.lower() ++ if not any(filename.endswith('.' + ext) for ext in allowed_extensions): ++ return jsonify({ ++ 'success': False, ++ 'message': '不支持的文件格式,请上传图片文件(png, jpg, gif, svg, ico, webp)' ++ }), 400 ++ ++ # 创建保存目录 ++ save_dir = 'static/logos' ++ os.makedirs(save_dir, exist_ok=True) ++ ++ # 生成安全的文件名 ++ import time ++ import hashlib ++ ext = os.path.splitext(filename)[1] ++ timestamp = str(int(time.time() * 1000)) ++ hash_name = hashlib.md5(f"{filename}{timestamp}".encode()).hexdigest()[:16] ++ safe_filename = f"logo_{hash_name}{ext}" ++ filepath = os.path.join(save_dir, safe_filename) ++ ++ # 保存文件 ++ file.save(filepath) ++ ++ # 返回相对路径 ++ return jsonify({ ++ 'success': True, ++ 'path': f'/{filepath.replace(os.sep, "/")}' ++ }) ++ ++ except Exception as e: ++ return jsonify({ ++ 'success': False, ++ 'message': f'上传失败: {str(e)}' ++ }), 500 ++ ++ @app.route('/api/generate-features', methods=['POST']) ++ @login_required ++ def generate_features(): ++ """使用DeepSeek自动生成网站主要功能""" ++ try: ++ data = request.get_json() ++ name = data.get('name', '').strip() ++ description = data.get('description', '').strip() ++ url = data.get('url', '').strip() ++ ++ if not name or not description: ++ return jsonify({ ++ 'success': False, ++ 'message': '请提供网站名称和描述' ++ }), 400 ++ ++ # 生成功能列表 ++ generator = TagGenerator() ++ features = generator.generate_features(name, description, url) ++ ++ if not features: ++ return jsonify({ ++ 'success': False, ++ 'message': 'DeepSeek功能生成失败,请检查API配置' ++ }), 500 ++ ++ return jsonify({ ++ 'success': True, ++ 'features': features ++ }) ++ ++ except ValueError as e: ++ return jsonify({ ++ 'success': False, ++ 'message': str(e) ++ }), 400 ++ except Exception as e: ++ return jsonify({ ++ 'success': False, ++ 'message': f'生成失败: {str(e)}' ++ }), 500 ++ ++ @app.route('/api/generate-description', methods=['POST']) ++ @login_required ++ def generate_description(): ++ """使用DeepSeek自动生成网站详细介绍""" ++ try: ++ data = request.get_json() ++ name = data.get('name', '').strip() ++ short_desc = data.get('short_desc', '').strip() ++ url = data.get('url', '').strip() ++ ++ if not name: ++ return jsonify({ ++ 'success': False, ++ 'message': '请提供网站名称' ++ }), 400 ++ ++ # 生成详细介绍 ++ generator = TagGenerator() ++ description = generator.generate_description(name, short_desc, url) ++ ++ if not description: ++ return jsonify({ ++ 'success': False, ++ 'message': 'DeepSeek详细介绍生成失败,请检查API配置' ++ }), 500 ++ ++ return jsonify({ ++ 'success': True, ++ 'description': description ++ }) ++ ++ except ValueError as e: ++ return jsonify({ ++ 'success': False, ++ 'message': str(e) ++ }), 400 ++ except Exception as e: ++ return jsonify({ ++ 'success': False, ++ 'message': f'生成失败: {str(e)}' ++ }), 500 ++ + @app.route('/api/generate-tags', methods=['POST']) + @login_required + def generate_tags(): +@@ -345,11 +558,11 @@ def create_app(config_name='default'): + }) + continue + +- # 4. 下载Logo(失败不影响导入) ++ # 4. 下载Logo到本地(失败不影响导入) + logo_path = None + if info.get('logo_url'): + try: +- logo_path = fetcher.download_logo(info['logo_url']) ++ logo_path = fetcher.download_logo(info['logo_url'], save_dir='static/logos') + except Exception as e: + print(f"下载Logo失败 ({url}): {str(e)}") + # Logo下载失败不影响网站导入 +@@ -544,9 +757,47 @@ def create_app(config_name='default'): + import re + import random + from pypinyin import lazy_pinyin ++ from flask import request + + # 使用no_autoflush防止在查询时触发提前flush + with db.session.no_autoflush: ++ # 处理手动输入的新标签 ++ new_tags_str = request.form.get('new_tags', '') ++ if new_tags_str: ++ new_tag_names = [name.strip() for name in new_tags_str.split(',') if name.strip()] ++ for tag_name in new_tag_names: ++ # 检查标签是否已存在 ++ existing_tag = Tag.query.filter_by(name=tag_name).first() ++ if not existing_tag: ++ # 创建新标签 ++ tag_slug = ''.join(lazy_pinyin(tag_name)) ++ tag_slug = tag_slug.lower() ++ tag_slug = re.sub(r'[^\w\s-]', '', tag_slug) ++ tag_slug = re.sub(r'[-\s]+', '-', tag_slug).strip('-') ++ ++ # 确保slug唯一 ++ base_tag_slug = tag_slug[:50] ++ counter = 1 ++ final_tag_slug = tag_slug ++ while Tag.query.filter_by(slug=final_tag_slug).first(): ++ final_tag_slug = f"{base_tag_slug}-{counter}" ++ counter += 1 ++ if counter > 100: ++ final_tag_slug = f"{base_tag_slug}-{random.randint(1000, 9999)}" ++ break ++ ++ new_tag = Tag(name=tag_name, slug=final_tag_slug) ++ db.session.add(new_tag) ++ db.session.flush() # 确保新标签有ID ++ ++ # 添加到模型的标签列表 ++ if new_tag not in model.tags: ++ model.tags.append(new_tag) ++ else: ++ # 添加已存在的标签 ++ if existing_tag not in model.tags: ++ model.tags.append(existing_tag) ++ + # 如果code为空,自动生成唯一的8位数字编码 + if not model.code or model.code.strip() == '': + max_attempts = 10 +@@ -684,6 +935,51 @@ def create_app(config_name='default'): + ] + } + ++ # Prompt模板管理视图 ++ class PromptAdmin(SecureModelView): ++ can_edit = True ++ can_delete = False # 不允许删除,避免系统必需的prompt被删除 ++ can_create = True ++ can_view_details = False ++ ++ # 显示操作列 ++ column_display_actions = True ++ ++ column_list = ['id', 'key', 'name', 'description', 'is_active', 'updated_at'] ++ column_searchable_list = ['key', 'name', 'description'] ++ column_filters = ['is_active', 'key'] ++ column_labels = { ++ 'id': 'ID', ++ 'key': '唯一标识', ++ 'name': '模板名称', ++ 'system_prompt': '系统提示词', ++ 'user_prompt_template': '用户提示词模板', ++ 'description': '模板说明', ++ 'is_active': '是否启用', ++ 'created_at': '创建时间', ++ 'updated_at': '更新时间' ++ } ++ form_columns = ['key', 'name', 'description', 'system_prompt', 'user_prompt_template', 'is_active'] ++ ++ # 字段说明 ++ column_descriptions = { ++ 'key': '唯一标识,如: tags, features, description', ++ 'system_prompt': 'AI的系统角色设定', ++ 'user_prompt_template': '用户提示词模板,支持变量如 {name}, {description}, {url}', ++ } ++ ++ # 表单字段配置 ++ form_widget_args = { ++ 'system_prompt': { ++ 'rows': 3, ++ 'style': 'font-family: monospace;' ++ }, ++ 'user_prompt_template': { ++ 'rows': 20, ++ 'style': 'font-family: monospace;' ++ } ++ } ++ + # 初始化 Flask-Admin + admin = Admin( + app, +@@ -696,6 +992,7 @@ def create_app(config_name='default'): + admin.add_view(SiteAdmin(Site, db.session, name='网站管理')) + admin.add_view(TagAdmin(Tag, db.session, name='标签管理')) + admin.add_view(NewsAdmin(News, db.session, name='新闻管理')) ++ admin.add_view(PromptAdmin(PromptTemplate, db.session, name='Prompt管理')) + admin.add_view(AdminAdmin(AdminModel, db.session, name='管理员', endpoint='admin_users')) + + return app +diff --git a/export_data.py b/export_data.py +new file mode 100644 +index 0000000..df212a9 +--- /dev/null ++++ b/export_data.py +@@ -0,0 +1,58 @@ ++# -*- coding: utf-8 -*- ++""" ++数据导出脚本 ++导出现有数据到SQL文件 ++""" ++import subprocess ++import os ++from datetime import datetime ++ ++def export_database(): ++ """导出数据库到SQL文件""" ++ # 从.env读取数据库配置 ++ from dotenv import load_dotenv ++ load_dotenv() ++ ++ db_host = os.getenv('DB_HOST', 'localhost') ++ db_port = os.getenv('DB_PORT', '3306') ++ db_user = os.getenv('DB_USER') ++ db_password = os.getenv('DB_PASSWORD') ++ db_name = os.getenv('DB_NAME') ++ ++ # 生成备份文件名 ++ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') ++ backup_file = f'backup_{db_name}_{timestamp}.sql' ++ ++ # mysqldump命令 ++ cmd = [ ++ 'mysqldump', ++ '-h', db_host, ++ '-P', db_port, ++ '-u', db_user, ++ f'-p{db_password}', ++ '--single-transaction', ++ '--routines', ++ '--triggers', ++ db_name ++ ] ++ ++ print(f"正在导出数据库 {db_name} ...") ++ ++ try: ++ with open(backup_file, 'w', encoding='utf-8') as f: ++ subprocess.run(cmd, stdout=f, stderr=subprocess.PIPE, check=True) ++ ++ file_size = os.path.getsize(backup_file) / 1024 # KB ++ print(f"✓ 数据库导出成功!") ++ print(f" 文件: {backup_file}") ++ print(f" 大小: {file_size:.2f} KB") ++ print(f"\n请将此文件上传到服务器进行恢复") ++ ++ except subprocess.CalledProcessError as e: ++ print(f"✗ 导出失败: {e.stderr.decode()}") ++ except FileNotFoundError: ++ print("✗ 错误: 找不到 mysqldump 命令") ++ print(" 请确保MySQL客户端工具已安装并在PATH中") ++ ++if __name__ == '__main__': ++ export_database() +diff --git a/migrate_db.py b/migrate_db.py +new file mode 100644 +index 0000000..4d4bdef +--- /dev/null ++++ b/migrate_db.py +@@ -0,0 +1,49 @@ ++# -*- coding: utf-8 -*- ++""" ++数据库安全迁移脚本 ++只创建缺失的表,不删除现有数据 ++用于生产环境部署 ++""" ++ ++import sys ++from app import create_app ++from models import db, Admin ++ ++def migrate_database(): ++ """安全迁移数据库(不删除数据)""" ++ app = create_app('production') ++ ++ with app.app_context(): ++ print("正在检查数据库表...") ++ ++ # 只创建不存在的表(不会删除数据) ++ db.create_all() ++ print("✓ 数据库表检查完成") ++ ++ # 检查是否存在管理员 ++ admin_count = Admin.query.count() ++ ++ if admin_count == 0: ++ print("\n未找到管理员账号,正在创建默认管理员...") ++ admin = Admin( ++ username='admin', ++ email='admin@example.com', ++ is_active=True ++ ) ++ admin.set_password('admin123') ++ db.session.add(admin) ++ db.session.commit() ++ ++ print("✓ 默认管理员创建成功") ++ print(" 用户名: admin") ++ print(" 密码: admin123") ++ print("\n⚠️ 请立即登录后台修改密码!") ++ else: ++ print(f"\n✓ 已存在 {admin_count} 个管理员账号") ++ ++ print("\n" + "="*50) ++ print("数据库迁移完成!") ++ print("="*50) ++ ++if __name__ == '__main__': ++ migrate_database() +diff --git a/migrate_prompts.py b/migrate_prompts.py +new file mode 100644 +index 0000000..a6bd693 +--- /dev/null ++++ b/migrate_prompts.py +@@ -0,0 +1,141 @@ ++"""创建 prompt_templates 表并初始化默认数据""" ++import os ++import sys ++from app import create_app ++from models import db, PromptTemplate ++ ++# 设置输出编码为UTF-8 ++if sys.platform.startswith('win'): ++ sys.stdout.reconfigure(encoding='utf-8') ++ ++def migrate_prompts(): ++ """创建表并初始化默认prompt模板""" ++ app = create_app(os.getenv('FLASK_ENV', 'development')) ++ ++ with app.app_context(): ++ # 创建表 ++ print("正在创建 prompt_templates 表...") ++ db.create_all() ++ print("[OK] 表创建成功") ++ ++ # 检查是否已有数据 ++ existing_count = PromptTemplate.query.count() ++ if existing_count > 0: ++ print(f"[WARN] 表中已有 {existing_count} 条数据,跳过初始化") ++ return ++ ++ # 初始化默认prompt模板 ++ print("正在初始化默认prompt模板...") ++ ++ # 1. 标签生成模板 ++ tags_prompt = PromptTemplate( ++ key='tags', ++ name='标签生成', ++ description='根据网站名称和描述生成3-5个分类标签', ++ system_prompt='你是一个专业的AI工具分类专家,擅长为各类AI产品生成准确的标签。', ++ user_prompt_template='''你是一个AI工具导航网站的标签生成助手。根据以下产品信息,生成3-5个最合适的标签。 ++ ++产品名称: {name} ++ ++产品描述: {description} ++{existing_tags} ++ ++要求: ++1. 标签应该准确描述产品的功能、类型或应用场景 ++2. 每个标签2-4个汉字 ++3. 标签要具体且有区分度 ++4. 如果是AI工具,可以标注具体的AI类型(如"GPT"、"图像生成"等) ++5. 只返回标签,用逗号分隔,不要其他说明 ++ ++示例输出格式:写作助手,营销,GPT,内容生成 ++ ++请生成标签:''', ++ is_active=True ++ ) ++ ++ # 2. 主要功能生成模板 ++ features_prompt = PromptTemplate( ++ key='features', ++ name='主要功能生成', ++ description='根据网站名称和描述生成5-8个主要功能点', ++ system_prompt='你是一个专业的AI产品文案专家,擅长提炼产品核心功能和价值点。', ++ user_prompt_template='''你是一个AI工具导航网站的内容编辑助手。根据以下产品信息,生成详细的主要功能列表。 ++ ++产品名称: {name} ++ ++产品描述: {description} ++{url_info} ++ ++要求: ++1. 生成5-8个主要功能点 ++2. 每个功能点要具体、清晰、有吸引力 ++3. 使用Markdown无序列表格式(以"- "开头) ++4. 每个功能点一行,简洁有力(10-30字) ++5. 突出产品的核心价值和特色功能 ++6. 使用专业但易懂的语言 ++7. 不要添加任何标题或额外说明,直接输出功能列表 ++ ++示例输出格式: ++- 智能文本生成,支持多种写作场景 ++- 实时语法检查和优化建议 ++- 多语言翻译,准确率高达95% ++- 一键生成营销文案和广告语 ++- 团队协作,支持多人同时编辑 ++ ++请生成功能列表:''', ++ is_active=True ++ ) ++ ++ # 3. 详细介绍生成模板 ++ description_prompt = PromptTemplate( ++ key='description', ++ name='详细介绍生成', ++ description='根据网站名称和简短描述生成200-400字的详细介绍', ++ system_prompt='你是一个专业的AI产品文案专家,擅长撰写准确、客观、有吸引力的产品介绍。', ++ user_prompt_template='''你是一个AI工具导航网站的内容编辑助手。根据以下产品信息,生成详细、专业且吸引人的产品介绍。 ++ ++产品名称: {name} ++{short_desc_info} ++{url_info} ++ ++要求: ++1. 生成200-400字的详细介绍 ++2. 包含以下内容: ++ - 产品定位和核心价值(这是什么产品,解决什么问题) ++ - 主要特点和优势(为什么选择这个产品) ++ - 适用场景和目标用户(谁会用,用在哪里) ++3. 使用Markdown格式,可以包含: ++ - 段落分隔(空行) ++ - 加粗重点内容(**文字**) ++ - 列表(- 列表项) ++4. 语言专业但易懂,突出产品价值 ++5. 不要添加标题,直接输出正文内容 ++6. 语气客观、事实性强,避免过度营销 ++ ++示例输出格式: ++ChatGPT是由OpenAI开发的**先进对话式AI助手**,基于GPT-4大语言模型构建。它能够理解和生成自然语言,为用户提供智能对话、内容创作、代码编写等多种服务。 ++ ++**核心优势:** ++- 强大的语言理解和生成能力 ++- 支持多轮对话,上下文连贯 ++- 覆盖编程、写作、翻译等多个领域 ++ ++适用于内容创作者、程序员、学生等各类用户,可用于日常问答、文案撰写、学习辅导、编程助手等多种场景。 ++ ++请生成详细介绍:''', ++ is_active=True ++ ) ++ ++ # 添加到数据库 ++ db.session.add(tags_prompt) ++ db.session.add(features_prompt) ++ db.session.add(description_prompt) ++ db.session.commit() ++ ++ print("[OK] 默认prompt模板初始化成功") ++ print(f" - 标签生成: {tags_prompt.id}") ++ print(f" - 主要功能生成: {features_prompt.id}") ++ print(f" - 详细介绍生成: {description_prompt.id}") ++ ++if __name__ == '__main__': ++ migrate_prompts() +diff --git a/models.py b/models.py +index 57e988a..fae1c97 100644 +--- a/models.py ++++ b/models.py +@@ -136,3 +136,35 @@ class Admin(UserMixin, db.Model): + + def __repr__(self): + return f'' ++ ++class PromptTemplate(db.Model): ++ """AI提示词模板模型""" ++ __tablename__ = 'prompt_templates' ++ ++ id = db.Column(db.Integer, primary_key=True) ++ key = db.Column(db.String(50), unique=True, nullable=False, comment='唯一标识(tags/features/description)') ++ name = db.Column(db.String(100), nullable=False, comment='模板名称') ++ system_prompt = db.Column(db.Text, nullable=False, comment='系统提示词') ++ user_prompt_template = db.Column(db.Text, nullable=False, comment='用户提示词模板(支持变量)') ++ description = db.Column(db.String(200), comment='模板说明') ++ is_active = db.Column(db.Boolean, default=True, comment='是否启用') ++ created_at = db.Column(db.DateTime, default=datetime.now, comment='创建时间') ++ updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间') ++ ++ def __repr__(self): ++ return f'' ++ ++ def to_dict(self): ++ """转换为字典""" ++ return { ++ 'id': self.id, ++ 'key': self.key, ++ 'name': self.name, ++ 'system_prompt': self.system_prompt, ++ 'user_prompt_template': self.user_prompt_template, ++ 'description': self.description, ++ 'is_active': self.is_active, ++ 'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else None, ++ 'updated_at': self.updated_at.strftime('%Y-%m-%d %H:%M:%S') if self.updated_at else None ++ } ++ +diff --git a/templates/admin/site/create.html b/templates/admin/site/create.html +index f32bed6..ed0597f 100644 +--- a/templates/admin/site/create.html ++++ b/templates/admin/site/create.html +@@ -11,6 +11,28 @@ + margin-top: 10px; + margin-bottom: 15px; + } ++ .generate-features-btn { ++ margin-top: 10px; ++ margin-bottom: 15px; ++ } ++ .upload-logo-btn { ++ margin-top: 10px; ++ margin-bottom: 15px; ++ } ++ .logo-preview { ++ margin-top: 10px; ++ max-width: 200px; ++ max-height: 200px; ++ display: none; ++ border: 1px solid #ddd; ++ border-radius: 8px; ++ padding: 10px; ++ } ++ .logo-preview img { ++ max-width: 100%; ++ max-height: 150px; ++ object-fit: contain; ++ } + .fetch-status { + margin-top: 10px; + padding: 10px; +@@ -47,6 +69,26 @@ + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } + } ++ /* 标签输入框样式 */ ++ .tag-input-wrapper { ++ margin-top: 10px; ++ } ++ .tag-input-field { ++ width: 100%; ++ padding: 8px 12px; ++ border: 1px solid #DCDFE6; ++ border-radius: 4px; ++ font-size: 14px; ++ } ++ .tag-input-field:focus { ++ outline: none; ++ border-color: #0052D9; ++ } ++ .tag-input-help { ++ margin-top: 5px; ++ font-size: 12px; ++ color: #606266; ++ } + + + + {% endblock %} +diff --git a/templates/admin/site/edit.html b/templates/admin/site/edit.html +index aef6d86..6594b5f 100644 +--- a/templates/admin/site/edit.html ++++ b/templates/admin/site/edit.html +@@ -11,6 +11,28 @@ + margin-top: 10px; + margin-bottom: 15px; + } ++ .generate-features-btn { ++ margin-top: 10px; ++ margin-bottom: 15px; ++ } ++ .upload-logo-btn { ++ margin-top: 10px; ++ margin-bottom: 15px; ++ } ++ .logo-preview { ++ margin-top: 10px; ++ max-width: 200px; ++ max-height: 200px; ++ display: none; ++ border: 1px solid #ddd; ++ border-radius: 8px; ++ padding: 10px; ++ } ++ .logo-preview img { ++ max-width: 100%; ++ max-height: 150px; ++ object-fit: contain; ++ } + .fetch-status { + margin-top: 10px; + padding: 10px; +@@ -47,6 +69,26 @@ + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } + } ++ /* 标签输入框样式 */ ++ .tag-input-wrapper { ++ margin-top: 10px; ++ } ++ .tag-input-field { ++ width: 100%; ++ padding: 8px 12px; ++ border: 1px solid #DCDFE6; ++ border-radius: 4px; ++ font-size: 14px; ++ } ++ .tag-input-field:focus { ++ outline: none; ++ border-color: #0052D9; ++ } ++ .tag-input-help { ++ margin-top: 5px; ++ font-size: 12px; ++ color: #606266; ++ } + + + ++ + {% block extra_js %}{% endblock %} + + +diff --git a/templates/detail_new.html b/templates/detail_new.html +index 1033f7c..c84192f 100644 +--- a/templates/detail_new.html ++++ b/templates/detail_new.html +@@ -20,12 +20,6 @@ + color: var(--text-primary); + } + +- .back-link .material-symbols-outlined { +- font-size: 24px; +- line-height: 1; +- vertical-align: middle; +- margin-top: -2px; +- } + + /* 产品头部区域 */ + .product-header-wrapper { +@@ -84,9 +78,6 @@ + text-decoration: underline; + } + +- .product-link .material-symbols-outlined { +- font-size: 16px; +- } + + .product-meta { + display: flex; +@@ -103,9 +94,6 @@ + font-size: 14px; + } + +- .meta-item .material-symbols-outlined { +- font-size: 18px; +- } + + .product-tags-list { + display: flex; +@@ -195,9 +183,6 @@ + box-shadow: 0 4px 12px rgba(14, 165, 233, 0.3); + } + +- .visit-btn .material-symbols-outlined { +- font-size: 18px; +- } + + .visit-hint { + text-align: center; +@@ -242,10 +227,6 @@ + margin-bottom: 20px; + } + +- .content-block h2 .material-symbols-outlined { +- font-size: 24px; +- color: var(--primary-blue); +- } + + .content-block p { + color: var(--text-secondary); +@@ -399,6 +380,126 @@ + font-weight: 500; + } + ++ /* Markdown内容样式 */ ++ .markdown-content { ++ color: var(--text-secondary); ++ line-height: 1.8; ++ } ++ ++ .markdown-content h1, ++ .markdown-content h2, ++ .markdown-content h3 { ++ color: var(--text-primary); ++ font-weight: 600; ++ margin-top: 24px; ++ margin-bottom: 16px; ++ line-height: 1.4; ++ } ++ ++ .markdown-content h1 { ++ font-size: 24px; ++ } ++ ++ .markdown-content h2 { ++ font-size: 20px; ++ } ++ ++ .markdown-content h3 { ++ font-size: 18px; ++ } ++ ++ .markdown-content p { ++ margin-bottom: 16px; ++ line-height: 1.8; ++ } ++ ++ .markdown-content ul, ++ .markdown-content ol { ++ margin: 16px 0; ++ padding-left: 24px; ++ } ++ ++ .markdown-content ul li { ++ list-style: none; ++ position: relative; ++ padding-left: 20px; ++ margin-bottom: 12px; ++ line-height: 1.8; ++ } ++ ++ .markdown-content ul li:before { ++ content: "▸"; ++ position: absolute; ++ left: 0; ++ color: var(--primary-blue); ++ font-weight: bold; ++ } ++ ++ .markdown-content ol li { ++ margin-bottom: 12px; ++ line-height: 1.8; ++ padding-left: 8px; ++ } ++ ++ .markdown-content code { ++ background: #f1f5f9; ++ color: #e11d48; ++ padding: 2px 6px; ++ border-radius: 4px; ++ font-family: 'Consolas', 'Monaco', 'Courier New', monospace; ++ font-size: 0.9em; ++ } ++ ++ .markdown-content pre { ++ background: #1e293b; ++ color: #e2e8f0; ++ padding: 16px; ++ border-radius: 8px; ++ overflow-x: auto; ++ margin: 16px 0; ++ } ++ ++ .markdown-content pre code { ++ background: transparent; ++ color: inherit; ++ padding: 0; ++ border-radius: 0; ++ } ++ ++ .markdown-content strong { ++ font-weight: 600; ++ color: var(--text-primary); ++ } ++ ++ .markdown-content em { ++ font-style: italic; ++ } ++ ++ .markdown-content a { ++ color: var(--primary-blue); ++ text-decoration: none; ++ border-bottom: 1px solid transparent; ++ transition: border-color 0.2s; ++ } ++ ++ .markdown-content a:hover { ++ border-bottom-color: var(--primary-blue); ++ } ++ ++ .markdown-content blockquote { ++ border-left: 4px solid var(--primary-blue); ++ padding-left: 16px; ++ margin: 16px 0; ++ color: var(--text-secondary); ++ font-style: italic; ++ } ++ ++ .markdown-content hr { ++ border: none; ++ border-top: 1px solid var(--border-color); ++ margin: 24px 0; ++ } ++ + /* 响应式 */ + @media (max-width: 968px) { + .product-header-wrapper { +@@ -432,7 +533,7 @@ + + + +- arrow_back ++ + 返回首页 + + +@@ -456,16 +557,16 @@ +

{{ site.name }}

+ + {{ site.url }} +- open_in_new ++ + + +
+
+- visibility ++ 👁 + {{ site.view_count | default(0) }} 次浏览 +
+
+- calendar_today ++ 📅 + 添加于 {{ site.created_at.strftime('%Y年%m月%d日') }} +
+
+@@ -488,7 +589,7 @@ + + + 访问网站 +- north_east ++ + +

在新标签页打开 • {{ site.url.split('/')[2] if site.url else '' }}

+ +@@ -501,20 +602,20 @@ + +
+

+- info ++ ℹ️ + 产品概述 +

+-

{{ site.description }}

++
{{ site.description | markdown | safe }}
+
+ + + {% if site.features %} +
+

+- description +- 详细描述 ++ 📋 ++ 主要功能 +

+-
{{ site.features | safe }}
++
{{ site.features | markdown | safe }}
+
+ {% endif %} + +@@ -522,7 +623,7 @@ + {% if news_list %} +
+

+- newspaper ++ 📰 + 相关新闻 +

+ {% for news in news_list %} +@@ -540,7 +641,7 @@ + {% if recommended_sites %} +
+

+- auto_awesome ++ + 相似推荐 +

+
+@@ -560,7 +661,7 @@ + {% endfor %} +
+
+- north_east ++ + + {% endfor %} +
+-- +2.50.1.windows.1 + diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..93fa2fd --- /dev/null +++ b/wsgi.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +""" +生产环境应用入口 +用于gunicorn启动 +""" +import os +from app import create_app + +# 创建应用实例 +app = create_app(os.getenv('FLASK_ENV', 'production')) + +if __name__ == '__main__': + # 这个分支仅用于直接运行脚本测试 + # 生产环境应该使用 gunicorn + app.run(host='0.0.0.0', port=5000, debug=False)