# 关键配置补充说明 - 部署文档勘误与增? > **文档版本?* v1.0 > **创建日期?* 2025-12-14 > **文档性质?* ?个独立部署文档的关键补充 > **优先级:** ⭐⭐⭐⭐?必读(包?个P0/P1致命问题? --- ## 📋 文档说明 本文档基于对5个独立部署文档的深度审查,补充了**3个致命问?*?*若干最佳实?*。这些内容在原文档中遗漏或未充分强调,但对生产环境部署至关重要? **请在部署前务必阅读本文档?* --- ## 🚨 致命问题修正(P0/P1? ### 1. SAE孤岛效应 - NAT网关配置 ⭐⭐⭐⭐? **问题严重度:P0(致命)** #### 问题描述 ``` SAE部署在VPC内,默认没有公网出口? 影响场景??后端调用 DeepSeek/OpenAI API ?超时 ?Python下载公网PDF ?超时 ?npm install公网依赖(构建时)→ 失败 结果:所有AI功能不可用,系统基本瘫痪?``` #### 解决方案 **方案A:NAT网关(推荐,生产环境?* ```bash # 步骤1:创建NAT网关 阿里云控制台 > VPC > NAT网关 > 创建NAT网关 ├─ VPC:选择SAE所在的VPC ├─ 交换机:选择SAE所在的交换?├─ 规格:小型(够用?└─ 计费方式:按使用量计? # 步骤2:创建并绑定EIP NAT网关详情 > 弹性公网IP > 绑定弹性公网IP ├─ 创建新EIP或选择已有EIP ├─ 带宽:按使用流量(成本低?└─ 确认绑定 # 步骤3:配置SNAT条目 NAT网关详情 > SNAT管理 > 创建SNAT条目 ├─ 选择交换机:SAE所在的交换机(?vsw-xxxxx?├─ 选择公网IP:刚才绑定的EIP └─ 确认创建 成本:NAT网关¥60/?+ EIP流量费?0-50/?= ¥90-110/?``` **方案B:SAE绑定公网IP(部分地域支持)** ```bash SAE控制?> 应用配置 > 网络配置 └─ 查看是否?公网访问"?绑定EIP"选项 ⚠️ 注意?- 并非所有地域都支持 - 优先使用方案A(更稳定?``` #### 验证NAT网关是否生效 ```bash # 方法1:在SAE应用日志中查?# 应用启动后,查看是否有DeepSeek API调用成功的日? # 方法2:通过云助手执行命令(SAE控制?> 实例列表 > 登录实例?curl -I https://api.deepseek.com # 应该返回 200 OK,而不是超? # 方法3:测试Python下载公网PDF curl -I https://arxiv.org/pdf/2301.00001.pdf # 应该返回 200 OK ``` #### 更新的文? - ?`00-部署架构总览.md`:物理架构图已增加NAT网关 - ?`00-部署架构总览.md`:成本估算已更新(?,200-1,250/月) - ⚠️ `05-Node.js后端-SAE容器部署指南.md`:需要在"SAE应用配置"章节增加网络配置说明 - ⚠️ `04-Python微服?SAE容器部署指南.md`:同? --- ### 2. 部署依赖死锁 - Dify API Key鸡生蛋问?⭐⭐⭐⭐? **问题严重度:P1(严重)** #### 问题描述 ``` 死锁链: 1. 后端启动需?DIFY_API_KEY 2. DIFY_API_KEY 需?Dify 启动并人工登录后才能生成 3. 后端如果健康检查失败,会无限重? 结果:后端无法启动,或启动后PKB模块不可?``` #### 解决方案(分阶段部署? **阶段1:首次部署后端(临时配置?* ```bash # SAE环境变量配置 DIFY_API_KEY=temp_placeholder_will_update_later # ⚠️ 重要:后端代码需要容错处?# backend/src/common/rag/DifyClient.ts constructor() { const apiKey = process.env.DIFY_API_KEY if (!apiKey || apiKey === 'temp' || apiKey.startsWith('temp_')) { console.warn('⚠️ Dify API Key未配置,PKB模块将不可用') this.enabled = false return } this.client = new DifySDK(apiKey) this.enabled = true } // 所有Dify调用前检?async createDataset(name: string) { if (!this.enabled) { throw new Error('Dify服务未配置,请先配置DIFY_API_KEY环境变量') } // ... 正常逻辑 } ``` **阶段2:部署Dify并获取真实Key** ```bash # 1. 部署Dify到ECS(参?03-Dify-ECS部署完全指南.md?cd /opt/dify docker-compose up -d # 2. 等待服务启动(约2-3分钟?docker-compose logs -f api # 3. 浏览器访?http://ECS公网IP # 4. 注册管理员账号(首次访问会提示) # 5. 创建API Key # 设置 > API密钥 > 创建密钥 > 复制 # 格式:app-xxxxxxxxxxxxxxxxxxxxx # 6. 记录API Key(妥善保存) DIFY_API_KEY=app-xxxxxxxxxxxxxxxxxxxxx ``` **阶段3:更新后端配?* ```bash # SAE控制?> 应用详情 > 环境变量 # 找到 DIFY_API_KEY,修改为真实?DIFY_API_KEY=app-xxxxxxxxxxxxxxxxxxxxx # 保存 > 重启应用 # SAE会执行滚动重启(零停机) ``` **阶段4:验证PKB功能** ```bash # 测试知识库创?curl -X POST https://your-api.com/api/v1/pkb/knowledge-bases \ -H "Authorization: Bearer YOUR_USER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"测试知识?,"description":"测试"}' # 应该返回 200 OK,而不?"Dify服务未配? 错误 ``` #### 更新的文? - ?`00-部署架构总览.md`:部署顺序已更新,明确分阶段部署 - ⚠️ `05-Node.js后端-SAE容器部署指南.md`:需要在"环境变量配置"章节增加临时配置说明 - ⚠️ `03-Dify-ECS部署完全指南.md`:需要在"首次访问"章节增加API Key生成步骤 --- ### 3. HTTP Client超时配置 - 防止连接泄漏 ⭐⭐⭐⭐ **问题严重度:P1(严重)** #### 问题描述 ``` Python服务处理PDF/OCR可能需?0-120?如果后端HTTP Client没有设置超时,会导致??连接数堆??后端实例内存耗尽 ?数据库连接池耗尽 ``` #### 解决方案 **后端HTTP Client配置** ```typescript // backend/src/common/http/httpClient.ts import axios from 'axios' export const pythonServiceClient = axios.create({ baseURL: process.env.EXTRACTION_SERVICE_URL || 'http://localhost:8000', timeout: 120000, // ⚠️ 120秒(2分钟? timeoutErrorMessage: 'Python微服务响应超时(>2分钟?, headers: { 'Content-Type': 'application/json' } }) // 请求拦截器(可选,用于日志?pythonServiceClient.interceptors.request.use( (config) => { console.log(`[HTTP] 调用Python服务: ${config.method?.toUpperCase()} ${config.url}`) return config }, (error) => Promise.reject(error) ) // 响应拦截器(错误处理?pythonServiceClient.interceptors.response.use( (response) => response, (error) => { if (error.code === 'ECONNABORTED') { console.error('[HTTP] Python服务超时:', error.message) } return Promise.reject(error) } ) ``` **Dify Client配置** ```typescript // backend/src/common/rag/DifyClient.ts import axios from 'axios' const difyHttpClient = axios.create({ baseURL: process.env.DIFY_API_URL || 'http://localhost/v1', timeout: 60000, // ⚠️ 60秒(Dify响应较快? headers: { 'Authorization': `Bearer ${process.env.DIFY_API_KEY}`, 'Content-Type': 'application/json' } }) ``` #### 超时时间建议 | 服务 | 超时时间 | 理由 | |------|---------|------| | **Python微服?* | 120?| PDF解析(Nougat OCR)可能需?0-120?| | **Dify API** | 60?| RAG检索通常<10秒,60秒足?| | **外部LLM API** | 60?| DeepSeek/OpenAI流式响应?0秒足?| | **数据库查?* | 30?| Prisma默认,复杂查询可能需?0-20?| #### 更新的文? - ⚠️ `05-Node.js后端-SAE容器部署指南.md`:需要在"代码准备"章节增加HTTP Client配置 --- ## ⚠️ 重要安全配置 ### 4. ECS端口安全 - Redis/Weaviate不对外开?⭐⭐⭐⭐? **问题严重度:P0(致命安全风险)** #### 问题描述 ``` Dify的Redis?379)和Weaviate?080)如果对公网开放: ?Redis无密码,可被攻击者直接访??Weaviate包含敏感的向量数??可能被用于DDoS攻击的跳?``` #### 正确配置 **docker-compose.yaml 端口配置** ```yaml services: # ?错误示例(危险) redis: ports: - "6379:6379" # 对所有网卡开放,包括公网? # ?正确配置 redis: image: redis:6-alpine ports: - "127.0.0.1:6379:6379" # 只监?localhost restart: always volumes: - ./volumes/redis/data:/data command: redis-server --save 60 1 --loglevel warning # ?正确配置 weaviate: image: semitechnologies/weaviate:1.19.0 ports: - "127.0.0.1:8080:8080" # 只监?localhost restart: always environment: - QUERY_DEFAULTS_LIMIT=25 - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true - PERSISTENCE_DATA_PATH=/var/lib/weaviate # ?只有Nginx需要对外(VPC内网? nginx: image: nginx:latest ports: - "80:80" # 对VPC内网开放(不是公网? restart: always volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf depends_on: - api - web ``` #### ECS安全组配? ```bash # 安全组规则(ECS控制?> 安全?> 配置规则? 入方向规则: ├─ 允许 80/TCP 来源:VPC网段?72.16.0.0/12? # Nginx ├─ 允许 22/TCP 来源:您的办公室IP # SSH管理 └─ 拒绝 所? 来源?.0.0.0/0 # 默认拒绝 出方向规则: └─ 允许 所? 目标?.0.0.0/0 # 允许访问公网 ``` #### 验证安全配置 ```bash # 从公网测试(应该失败?telnet ECS公网IP 6379 # 应该超时或拒绝连? telnet ECS公网IP 8080 # 应该超时或拒绝连? # 从VPC内测试(应该成功?# 在SAE应用中执?curl http://172.16.x.x # Dify内网地址 # 应该返回 Dify 的响?``` #### 更新的文? - ⚠️ `03-Dify-ECS部署完全指南.md`:需要在"docker-compose.yaml配置"章节强调端口安全 --- ### 5. Nginx client_max_body_size - 支持大文件上?⭐⭐⭐⭐ **问题严重度:P2(一般)** #### 问题描述 ``` 医疗PDF可能很大?0-50MB?Nginx默认限制?MB 结果:用户上传大文件时返?413 Request Entity Too Large ``` #### 解决方案 **前端Nginx配置** ```nginx # frontend-v2/nginx.conf.template http { # ⚠️ 新增:支持大文件上传 client_max_body_size 50M; # ⚠️ 新增:开启gzip(React大体积JS? gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_min_length 1000; gzip_comp_level 6; server { listen 8080; server_name _; # 根目? root /usr/share/nginx/html; index index.html; # API反向代理 location /api/ { proxy_pass http://${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # ⚠️ 新增:代理超时配? proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_read_timeout 120s; } # SPA路由 location / { try_files $uri $uri/ /index.html; } # 健康检? location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } } } ``` #### 更新的文? - ⚠️ `06-前端Nginx-SAE容器部署指南.md`:需要在"nginx.conf.template"章节增加配置 --- ### 6. Python Workers限制 - 防止OOM ⭐⭐⭐⭐? **问题严重度:P1(严重)** #### 问题描述 ``` PyMuPDF/Nougat OCR非常吃内存(单个请求可能占用500MB-1GB?SAE配置?GB内存 如果Gunicorn workers过多,会导致OOM(Out of Memory?``` #### 解决方案 **Dockerfile配置** ```dockerfile # extraction_service/Dockerfile # 运行阶段 FROM python:3.11-slim # ... 其他配置 ... # ⚠️ 关键:限制workers防止OOM CMD ["gunicorn", "app.main:app", \ "--workers", "2", \ "--worker-class", "uvicorn.workers.UvicornWorker", \ "--bind", "0.0.0.0:8000", \ "--timeout", "120", \ "--max-requests", "100", \ "--max-requests-jitter", "10", \ "--access-logfile", "-", \ "--error-logfile", "-"] # workers=2: 最?个worker?GB内存限制?# timeout=120: 单个请求最?20秒(OCR可能很慢?# max-requests=100: 100个请求后重启worker(防止内存泄漏) ``` **SAE配置** ```bash # SAE控制?> 应用配置 > 实例规格 CPU: 1?内存: 2GB # ⚠️ 不要低于2GB # 实例数量 最小实例数: 1 最大实例数: 3(根据流量自动扩容) ``` #### Workers数量计算公式 ```python # 经验公式 workers = (CPU核数 × 2) + 1 # 但对于内存密集型应用(如PDF解析?workers = min((内存GB / 单worker内存GB), (CPU核数 × 2) + 1) # 示例:SAE 1?GB 单worker内存 ?800MB(PyMuPDF + Nougat?workers = min(2GB / 0.8GB, 1×2+1) = min(2.5, 3) = 2 # 结论:workers=2 是安全?``` #### 监控OOM ```bash # SAE控制?> 监控 > 内存使用?# 如果经常达到90%+,说明需要: # 1. 减少workers(从2降到1?# 2. 增加内存(从2GB升到4GB?# 3. 优化代码(减少内存占用) ``` #### 更新的文? - ⚠️ `04-Python微服?SAE容器部署指南.md`:需要在"Dockerfile"章节强调workers限制 --- ## 📖 开发调试最佳实? ### 7. SSH隧道 - 本地直连RDS数据?⭐⭐⭐⭐ **用途:开发便利性(非必需,但强烈推荐?* #### 场景 ``` 开发人员需要用Navicat/DBeaver查看RDS数据 但RDS只允许VPC内网访问 解决:通过ECS作为跳板机,建立SSH隧道 ``` #### 操作步骤 **步骤1:确保ECS有SSH访问权限** ```bash # 本地生成SSH密钥(如果还没有?ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # 将公钥添加到ECS # ECS控制?> 实例 > 远程连接 > 重置密钥?# 或者手动添加到 ~/.ssh/authorized_keys ``` **步骤2:建立SSH隧道** ```bash # 格式 ssh -N -L 本地端口:RDS内网地址:RDS端口 root@ECS公网IP -i 密钥文件 # 示例 ssh -N -L 5433:rm-bp1xxxxx.pg.rds.aliyuncs.com:5432 \ root@120.55.xx.xx \ -i ~/.ssh/dify-ecs.pem # 参数说明?# -N: 不执行远程命令,只建立隧?# -L: 本地端口转发 # 5433: 本地监听端口(避免与本地PostgreSQL 5432冲突?# rm-bp1xxxxx...: RDS内网地址 # 5432: RDS端口 ``` **步骤3:Navicat连接** ``` 连接类型:PostgreSQL 主机:localhost 端口?433 用户名:aiclinical_rw 密码:(RDS密码?数据库:ai_clinical_research 测试连接 ?成功?``` **步骤4:后台运行隧道(可选)** ```bash # 方法1:nohup后台运行 nohup ssh -N -L 5433:rm-xxxxx.pg.rds.aliyuncs.com:5432 \ root@ECS-IP -i key.pem > /dev/null 2>&1 & # 方法2:创建systemd服务(Linux?# /etc/systemd/system/rds-tunnel.service [Unit] Description=SSH Tunnel to RDS After=network.target [Service] Type=simple User=your-user ExecStart=/usr/bin/ssh -N -L 5433:rm-xxxxx.pg.rds.aliyuncs.com:5432 root@ECS-IP -i /home/your-user/.ssh/key.pem Restart=always [Install] WantedBy=multi-user.target # 启动服务 sudo systemctl start rds-tunnel sudo systemctl enable rds-tunnel ``` #### 安全注意事项 ``` ⚠️ 不要将SSH密钥提交到Git ⚠️ 定期轮换ECS的SSH密钥 ⚠️ 只在开发环境使用,生产环境通过VPN访问 ``` --- ### 8. 时区统一配置 - 防止日志时间混乱 ⭐⭐⭐⭐? **问题严重度:P2(重要)** #### 问题描述 ``` 不同服务的时区不一致会导致??日志时间对不上(前端14:00,后?6:00??pg-boss定时任务在错误时间触??用户看到的时间戳错误 ?排查故障极为痛苦 ``` #### 解决方案 **所有服务统一使用 `Asia/Shanghai` 时区** ```dockerfile # backend/Dockerfile - Node.js后端 FROM node:22-alpine RUN apk add --no-cache tzdata ENV TZ=Asia/Shanghai # ... 其他配置 # extraction_service/Dockerfile - Python微服?FROM python:3.11-slim RUN apt-get update && apt-get install -y tzdata ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # ... 其他配置 # frontend-v2/Dockerfile - 前端(已配置?FROM nginx:1.25-alpine RUN apk add --no-cache tzdata ENV TZ=Asia/Shanghai # ... 其他配置 ``` ```sql -- RDS PostgreSQL 时区配置 -- RDS控制?> 参数设置 > timezone timezone = Asia/Shanghai ``` **验证时区?* ```bash # 查看容器时区 docker exec date # 应该显示:Sat Dec 14 14:30:00 CST 2024 # 查看RDS时区 psql -c "SHOW timezone;" # 应该显示:Asia/Shanghai ``` #### 影响范围 - ?Node.js后端:需要更新Dockerfile - ?Python微服务:需要更新Dockerfile - ?前端Nginx:已正确配置 - ?RDS PostgreSQL:需要修改参? #### 修复步骤 ```bash # 1. 修改Node.js后端Dockerfile cd backend # 在Dockerfile中添加时区配置(见上方示例) # 2. 修改Python微服务Dockerfile cd extraction_service # 在Dockerfile中添加时区配置(见上方示例) # 3. 修改RDS时区 # RDS控制?> 参数设置 > timezone > Asia/Shanghai # 需要重启RDS实例 # 4. 验证 docker exec backend-container date docker exec python-container date psql -h rds-host -c "SHOW timezone;" ``` --- ### 9. 镜像拉取策略 - 防止代码不更?⭐⭐⭐⭐? **问题严重度:P2(重要)** #### 问题描述 ``` 场景?开发者修改代??构建镜像 ?推送到ACR(覆盖v1.0.0??SAE部署 ?发现代码没更新??? 原因?SAE默认镜像拉取策略可能?IfNotPresent 如果本地已有 v1.0.0,不会重新拉?``` #### 解决方案 **方案A:每次部署使用新版本号(强烈推荐?* ```bash # 使用语义化版本号 v1.0.0 ?v1.0.1 ?v1.0.2 ... # 或使用时间戳 v20251214-1430 ?v20251214-1530 ... # 或使用Git SHA v-a1b2c3d ?v-b2c3d4e ... # 构建示例 docker build -t backend:v1.0.1 . docker tag backend:v1.0.1 registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.1 docker push registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.1 # SAE部署时选择新版本号 ``` **方案B:配置SAE镜像拉取策略(测试环境)** ```bash # SAE控制?> 应用配置 > 镜像设置 镜像拉取策略:Always # ⚠️ 注意?# - 每次重启都会拉取镜像(启动稍慢) # - 适合测试环境,不推荐生产环境 ``` #### 最佳实? | 环境 | 推荐方案 | 理由 | |------|---------|------| | **生产环境** | 方案A(版本号管理?| 版本可追溯,稳定 | | **测试环境** | 方案B(Always拉取?| 始终最新,方便 | | **开发环?* | 方案A | 避免混乱 | **?不要?* ```bash # 始终使用 latest 标签(无法追溯版本) docker tag backend:latest ... ``` --- ### 10. Python服务内存管理 - 防止OOM ⭐⭐⭐⭐ **问题严重度:P2(重要)** #### 问题描述 ``` Python服务(PyMuPDF/Nougat)内存密集,容易OOM ?单个PDF OCR可能占用500MB-1GB内存 ?多个并发请求会导致内存溢??SAE默认2GB内存可能不够 ``` #### 解决方案 **规格建议?* | 场景 | CPU | 内存 | Workers | 适用情况 | |------|-----|------|---------|---------| | **基础?* | 1?| 2GB | 2 | 简单PDF解析 | | **标准?* | 2?| 4GB | 3 | 包含OCR(Nougat?| | **增强?* | 2?| 8GB | 4 | 大量OCR + 高并?| **Dockerfile优化(已应用):** ```dockerfile # extraction_service/Dockerfile CMD ["gunicorn", "main:app", \ "--bind", "0.0.0.0:8000", \ "--workers", "2", \ # ⚠️ 限制并发 "--timeout", "120", \ # ⚠️ 2分钟超时 "--max-requests", "100", \ # ⚠️ 处理100个请求后重启worker "--max-requests-jitter", "10"] # ⚠️ 随机抖动,避免同时重?``` **监控与告警:** ```bash # SAE控制?> 监控告警 > 创建告警规则 指标:内存使用率 阈值:> 80% 动作:发送通知 + 自动扩容(可选) ``` #### 如果遇到OOM **方案1:升级内存(推荐?* ```bash # SAE控制?> 应用配置 > 规格调整 1?GB ?2?GB(增加?00/月) ``` **方案2:限制并发(临时?* ```dockerfile # 修改Dockerfile CMD ["gunicorn", "main:app", \ "--workers", "1", \ # 降低并发 "--threads", "2"] # 使用线程而非进程 ``` --- ### 11. OSS签名URL - 安全的文件访?⭐⭐⭐⭐ **用途:安全最佳实?* #### 问题 ``` 如果OSS Bucket设置为Public??任何人都可以访问所有文??无法追踪谁访问了哪些文件 ?无法控制访问时效 ``` #### 解决方案 **OSS Bucket配置** ```bash # OSS控制?> Bucket列表 > aiclinical-data-prod # 读写权限:私有(Private?``` **后端生成签名URL** ```typescript // backend/src/common/storage/OSSAdapter.ts import OSS from 'ali-oss' export class OSSAdapter { private client: OSS constructor() { this.client = new OSS({ region: process.env.OSS_REGION!, accessKeyId: process.env.OSS_ACCESS_KEY_ID!, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET!, bucket: process.env.OSS_BUCKET! }) } /** * 生成签名URL?小时有效期) */ async getSignedUrl(objectKey: string, expiresIn: number = 3600): Promise { try { const url = this.client.signatureUrl(objectKey, { expires: expiresIn, // 秒,默认1小时 method: 'GET' }) return url } catch (error) { console.error('[OSS] 生成签名URL失败:', error) throw error } } /** * 上传文件 */ async uploadFile(objectKey: string, filePath: string): Promise { try { const result = await this.client.put(objectKey, filePath) console.log(`[OSS] 文件上传成功: ${objectKey}`) // 返回签名URL(不是公开URL? return this.getSignedUrl(objectKey) } catch (error) { console.error('[OSS] 文件上传失败:', error) throw error } } } ``` **API返回签名URL** ```typescript // backend/src/modules/pkb/documentController.ts router.get('/documents/:id/download', async (req, res) => { const { id } = req.params // 1. 查询文档元数? const document = await prisma.document.findUnique({ where: { id } }) if (!document) { return res.status(404).json({ error: '文档不存? }) } // 2. 权限校验(确保用户有权访问) if (document.userId !== req.user.id) { return res.status(403).json({ error: '无权访问此文? }) } // 3. 生成签名URL?小时有效? const ossAdapter = new OSSAdapter() const signedUrl = await ossAdapter.getSignedUrl(document.ossKey, 3600) // 4. 返回签名URL res.json({ url: signedUrl, expiresIn: 3600, filename: document.filename }) }) ``` **前端使用签名URL** ```typescript // frontend-v2/src/api/documents.ts export async function downloadDocument(documentId: string) { // 1. 调用后端API获取签名URL const response = await fetch(`/api/v1/pkb/documents/${documentId}/download`, { headers: { 'Authorization': `Bearer ${getToken()}` } }) const { url, filename } = await response.json() // 2. 使用签名URL下载文件 const link = document.createElement('a') link.href = url // 签名URL?小时有效 link.download = filename link.click() } ``` #### 优势 ``` ?安全:只有授权用户才能获取签名URL ?时效:URL自动过期?小时后失效) ?审计:可以记录谁访问了哪些文??灵活:可以动态调整过期时?``` --- ## 📝 总结 ### 必须立即修复的问? | # | 问题 | 严重?| 影响 | 修复时间 | |---|------|--------|------|---------| | 1 | **NAT网关缺失** | P0 | 所有AI功能不可?| 15分钟 | | 2 | **Dify API Key死锁** | P1 | PKB模块不可?| 10分钟(分阶段部署?| | 3 | **HTTP超时未配?* | P1 | 连接泄漏,系统崩?| 5分钟(代码修改) | | 4 | **ECS端口对外开?* | P0 | 安全风险,可被攻?| 5分钟(docker-compose修改?| | 5 | **Python Workers过多** | P1 | OOM,服务崩?| 2分钟(Dockerfile修改?| | 6 | **Nginx文件大小限制** | P2 | 大文件上传失?| 2分钟(nginx.conf修改?| ### 推荐但非必需的优? | # | 优化 | 价?| 实施时间 | |---|------|------|---------| | 7 | **SSH隧道** | 开发便利?| 10分钟 | | 8 | **OSS签名URL** | 安全最佳实?| 30分钟(代码修改) | ### 下一步行? ``` ?1. 创建NAT网关(必需?5分钟)⭐⭐⭐⭐⭐ ?2. 修改docker-compose.yaml(ECS端口安全?分钟)⭐⭐⭐⭐⭐ ?3. 修改Dockerfile(Python workers限制?分钟)⭐⭐⭐??4. 修改nginx.conf(文件大小限制,2分钟)⭐⭐⭐??5. 修改后端代码(HTTP超时?分钟)⭐⭐⭐??6. 修改后端代码(Dify容错?分钟)⭐⭐⭐??7. 更新部署流程(分阶段部署,文档更新)⭐⭐⭐⭐ ?8. 统一时区配置(必需?5分钟)⭐⭐⭐⭐⭐ ?9. 配置镜像拉取策略(必需?分钟)⭐⭐⭐⭐⭐ ?10. Python内存管理(必需?0分钟)⭐⭐⭐??11. (可选)配置SSH隧道(开发便利,10分钟??12. (可选)实现OSS签名URL(安全,30分钟? 总计:必需修复?0分钟,可选优化约40分钟 ``` --- **文档创建人:** AI助手 **最后更新:** 2025-12-14 **版本?* v1.0 **核心理念:安全第一、稳定第二、便利第?* ⭐⭐?