diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 85ee1a3..00fc3c3 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -76,6 +76,7 @@ jobs: with: sparse-checkout: | docker-compose.prod.yml + nginx/nginx.conf - name: Create .env file for deployment run: | @@ -109,6 +110,22 @@ jobs: cp ${DEPLOY_PATH}/.env ${DEPLOY_PATH}/backups/.env.$(date +%Y%m%d%H%M%S) fi fi + + # 创建nginx目录和日志目录(如果不存在) + mkdir -p ${DEPLOY_PATH}/nginx + mkdir -p ${DEPLOY_PATH}/nginx/logs + mkdir -p ${DEPLOY_PATH}/nginx/ssl + + # 如果SSL证书不存在,创建自签名证书(仅用于测试) + if [ ! -f ${DEPLOY_PATH}/nginx/ssl/cert.pem ] || [ ! -f ${DEPLOY_PATH}/nginx/ssl/key.pem ]; then + echo "SSL证书不存在,创建自签名证书..." + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout ${DEPLOY_PATH}/nginx/ssl/key.pem \ + -out ${DEPLOY_PATH}/nginx/ssl/cert.pem \ + -subj "/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" + echo "自签名证书创建完成" + fi - name: Copy files to server uses: appleboy/scp-action@master @@ -116,7 +133,7 @@ jobs: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} - source: "docker-compose.prod.yml,.env" + source: "docker-compose.prod.yml,.env,nginx/nginx.conf" target: ${{ secrets.DEPLOY_PATH }} overwrite: true @@ -139,7 +156,7 @@ jobs: sleep 10 # 验证服务是否正常运行 - if ! curl -s http://localhost:8888/api/config > /dev/null; then + if ! curl -s -k https://localhost:443 > /dev/null && ! curl -s http://localhost:80 > /dev/null; then echo "服务未正常运行!" exit 1 fi diff --git a/README.md b/README.md index 88e87fd..3de0ed9 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,41 @@ # 股票分析系统 (Stock Analysis System) ## 简介 -基于 https://github.com/DR-lin-eng/stock-scanner 二次修改,感谢原作者 + +基于 https://github.com/DR-lin-eng/stock-scanner 二次修改,感谢原作者 ## 功能变更 -1. 增加html页面,支持浏览器在线使用。 -2. 增加港股、美股支持。 -3. 完善Dockerfile、GitHub Actions 支持docker一键部署使用。 -4. 支持x86_64 和 ARM64架构镜像 -5. 支持流式输出,支持前端传入Key(仅作为本地用户使用,日志等内容不会输出) 感谢@Cassianvale -6. 重构为Vue3+Vite+TS+Naive UI,支持响应式布局 -7. 支持GitHub Actions 一键部署 -## docker一键部署 +1. 增加html页面,支持浏览器在线使用 +2. 增加港股、美股支持 +3. 完善Dockerfile、GitHub Actions 支持docker一键部署使用 +4. 支持x86_64 和 ARM64架构镜像 +5. 支持流式输出,支持前端传入Key(仅作为本地用户使用,日志等内容不会输出) 感谢@Cassianvale +6. 重构为Vue3+Vite+TS+Naive UI,支持响应式布局 +7. 支持GitHub Actions 一键部署 +8. 支持Nginx反向代理,可通过80/443端口访问 + +## Docker镜像一键部署 + ``` +# 拉取最新版本 +docker pull cassianvale/stock-scanner:latest + +# 启动容器 docker run -d \ - --name stock-scanner \ + --name stock-scanner-app \ -p 8888:8888 \ - -e API_KEY=替换为你的key \ - -e API_URL=替换为你的api地址 \ - -e API_MODEL=替换为你的模型 \ - -e API_TIMEOUT=60 \ - -e LOGIN_PASSWORD=替换为你的密码 \ - lanzhihong/stock-scanner:latest + -e API_KEY=你的API密钥 \ + -e API_URL=你的API地址 \ + -e API_MODEL=你的API模型 \ + -e API_TIMEOUT=超时时间(默认60秒) \ + -e LOGIN_PASSWORD=登录密码(可选) \ + -e ANNOUNCEMENT_TEXT=公告文本 \ + -v $(pwd)/logs:/app/logs \ + --restart unless-stopped \ + --network bridge \ + cassianvale/stock-scanner:latest -API_TIMEOUT=60 202503040712版本开始 (AI分析发生错误,查看日志是否有timed out类似错误,需要增加你的API超时时间) -LOGIN_PASSWORD 为空时,表示不需要登录,否则需要经过登录接口验证 - -注意⚠️: 环境变量名变更,更新版本后需要调整!!! 针对API_URL处理兼容更多的api地址,规则与Cherry Studio一致, /结尾忽略v1版本,#结尾强制使用输入地址。 API_URL 处理逻辑说明: @@ -46,7 +54,54 @@ API_URL 处理逻辑说明: ``` -默认8888端口,部署完成后访问 http://127.0.0.1:8888 即可使用。 + +默认8888端口,部署完成后访问 http://你的域名或ip:8888 即可使用 + +## 使用Nginx反向代理 + +项目已集成Nginx服务,可以通过80端口(HTTP)和443端口(HTTPS)访问应用 +使用docker-compose启动: + +``` +# 克隆仓库 +git clone https://github.com/your-username/stock-scanner.git +cd stock-scanner + +# 创建.env文件并填写必要的环境变量 +cat > .env << EOL +API_KEY=你的API密钥 +API_URL=你的API地址 +API_MODEL=你的API模型 +API_TIMEOUT=超时时间(默认60秒) +LOGIN_PASSWORD=登录密码(可选) +ANNOUNCEMENT_TEXT=公告文本 +EOL + +# 创建SSL证书目录 +mkdir -p nginx/ssl + +# 生成自签名SSL证书(仅用于测试环境) +openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout nginx/ssl/key.pem \ + -out nginx/ssl/cert.pem \ + -subj "/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" + +# 启动服务 +docker-compose up -d +``` + +启动后可通过以下方式访问应用: +- HTTP: http://你的域名或ip +- HTTPS: https://你的域名或ip + +### 使用自己的SSL证书 + +如果您有自己的SSL证书,可以替换自签名证书: + +1. 将您的证书文件放在 `nginx/ssl/` 目录下 +2. 确保证书文件命名为 `cert.pem`,私钥文件命名为 `key.pem` +3. 重启服务: `docker-compose restart nginx` ## Github Actions 部署 @@ -65,6 +120,7 @@ API_URL 处理逻辑说明: - 股票分析仅供参考,不构成投资建议 - 使用前请确保网络连接正常 - 建议在实盘前充分测试 +- 使用HTTPS可以提高数据传输的安全性 ## 贡献 (Contributing) 欢迎提交 issues 和 pull requests! diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index be3355b..5945eb5 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -39,6 +39,41 @@ services: networks: - stock-scanner-network + nginx: + image: nginx:stable-alpine + container_name: stock-scanner-nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf + - ./nginx/logs:/var/log/nginx + - ./nginx/ssl:/etc/nginx/ssl + depends_on: + - app + restart: always + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + deploy: + resources: + limits: + cpus: '0.5' + memory: 256M + reservations: + cpus: '0.1' + memory: 128M + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + networks: + - stock-scanner-network + networks: stock-scanner-network: driver: bridge \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 585f28e..887c40d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,6 +27,28 @@ services: networks: - stock-scanner-network + nginx: + image: nginx:stable-alpine + container_name: stock-scanner-nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf + - ./nginx/logs:/var/log/nginx + - ./nginx/ssl:/etc/nginx/ssl + depends_on: + - app + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 5s + networks: + - stock-scanner-network + networks: stock-scanner-network: driver: bridge diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..ae27942 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,93 @@ +server { + listen 80; + listen 443 ssl; + server_name localhost; + + # SSL证书配置 + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # 日志配置 + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + + # 设置较大的客户端请求体大小限制,以支持上传文件 + client_max_body_size 20M; + + # 设置超时时间 + proxy_connect_timeout 300s; + proxy_send_timeout 300s; + proxy_read_timeout 300s; + + # 启用gzip压缩 + gzip on; + gzip_disable "msie6"; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_min_length 256; + gzip_types + application/javascript + application/json + application/x-javascript + application/xml + application/xml+rss + image/svg+xml + text/css + text/javascript + text/plain + text/xml; + + # 反向代理到应用服务 + location / { + proxy_pass http://app:8888; + 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"; + + # 安全相关头信息 + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Frame-Options SAMEORIGIN; + add_header Referrer-Policy strict-origin-when-cross-origin; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + } + + # API请求处理 + location /api/ { + proxy_pass http://app:8888; + 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"; + } + + # 静态文件缓存设置 + location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { + proxy_pass http://app:8888; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + expires 30d; + add_header Cache-Control "public, max-age=2592000"; + add_header X-Content-Type-Options nosniff; + } +} \ No newline at end of file