修改密码
+修改您的管理员账户登录密码
+-
+
- 密码修改成功后,您将被自动登出,需要使用新密码重新登录 +
- 请妥善保管您的密码,不要与他人分享 +
- 建议定期修改密码以保证账号安全 +
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 @@ + + +
+ + +修改您的管理员账户登录密码
+')
+ 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 %}
+