Files
ViGent2/Docs/DevLogs/Day11.md
Kevin Wong 4a3dd2b225 更新
2026-01-28 17:22:31 +08:00

279 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 🔧 上传架构重构 (Direct Upload)
### 🚨 问题描述 (10:30)
**现象**:上传大于 7MB 的文件时,后端返回 500 Internal Server Error实际为 `ClientDisconnect`
**ROOT CAUSE (关键原因)**
- **Aliyun Nginx 网关限制**`api.hbyrkj.top` 域名的 Nginx 配置缺少 `client_max_body_size 0;`
- **默认限制**Nginx 默认限制请求体为 1MB (或少量),导致大文件上传时连接被网关强制截断。
- **误判**:初期待查方向集中在 FRP 和 Backend Proxy 超时,实际是网关层的硬限制。
### ✅ 解决方案:前端直传 Supabase + 网关配置 (14:00)
**核心变更**
1. **网关配置**:在 Aliyun Nginx 的 `api.hbyrkj.top` 配置块中添加 `client_max_body_size 0;` (解除大小限制)。
2. **架构优化**:移除后端文件转发逻辑,改由前端直接上传到 Supabase Storage (减少链路节点)。
#### 1. 前端改造 (`frontend/src/app/page.tsx`)
- 引入 `@supabase/supabase-js` 客户端。
- 使用 `supabase.storage.from('materials').upload()` 直接上传。
- 移除旧的 `XMLHttpRequest` 代理上传逻辑。
- 添加文件重命名策略:`{timestamp}_{sanitized_filename}`
```typescript
// V2: Direct Upload (Bypass Backend)
const { data, error } = await supabase.storage
.from('materials')
.upload(path, file, {
cacheControl: '3600',
upsert: false
});
```
#### 2. 后端适配 (`backend/app/api/materials.py`)
- **上传接口**(已废弃/保留用于极小文件) 主要流量走直传。
- **列表接口**:更新为返回 **签名 URL (Signed URL)**,而非本地路径。
- **兼容性**:前端直接接收 `path` 字段为完整 URL无需再次拼接。
#### 3. 权限控制 (RLS)
- Supabase 默认禁止匿名写入。
- 执行 SQL 策略允许 `anon` 角色对 `materials` 桶的 `INSERT``SELECT` 权限。
```sql
-- Allow anonymous uploads
CREATE POLICY "Allow public uploads"
ON storage.objects FOR INSERT
TO anon WITH CHECK (bucket_id = 'materials');
```
### 结果
-**彻底解决超时**:上传不再经过 Nginx/FRP直接走 Supabase CDN。
-**解除大小限制**:不再受限于后端服务的 `client_max_body_size`
-**用户体验提升**:上传速度更快,进度条更准确。
## 🔧 Supabase 部署与 RLS 配置
### 相关文件
- `supabase_rls.sql`: 定义存储桶权限的 SQL 脚本。
- `docker-compose.yml`: 确认 Storage 服务配置正常。
### 操作步骤
1.`supabase_rls.sql` 上传至服务器。
2. 通过 Docker 执行 SQL
```bash
cat supabase_rls.sql | docker exec -i supabase-db psql -U postgres
```
3. 验证前端上传成功。
---
## 🔐 用户隔离实现 (15:00)
### 问题描述
不同账户登录后能看到其他用户上传的素材和生成的视频,缺乏数据隔离。
### 解决方案:存储路径前缀隔离
#### 1. 素材模块 (`backend/app/api/materials.py`)
```python
# 上传时添加用户ID前缀
storage_path = f"{user_id}/{timestamp}_{safe_name}"
# 列表时只查询当前用户目录
files_obj = await storage_service.list_files(
bucket=storage_service.BUCKET_MATERIALS,
path=user_id # 只列出用户目录下的文件
)
# 删除时验证权限
if not material_id.startswith(f"{user_id}/"):
raise HTTPException(403, "无权删除此素材")
```
#### 2. 视频模块 (`backend/app/api/videos.py`)
```python
# 生成视频时使用用户ID目录
storage_path = f"{user_id}/{task_id}_output.mp4"
# 列表/删除同样基于用户目录隔离
```
#### 3. 发布模块 (`backend/app/services/publish_service.py`)
- Cookie 存储支持用户隔离:`cookies/{user_id}/{platform}.json`
### 存储结构
```
Supabase Storage/
├── materials/
│ ├── {user_id_1}/
│ │ ├── 1737000001_video1.mp4
│ │ └── 1737000002_video2.mp4
│ └── {user_id_2}/
│ └── 1737000003_video3.mp4
└── outputs/
├── {user_id_1}/
│ └── {task_id}_output.mp4
└── {user_id_2}/
└── ...
```
### 结果
- ✅ 不同用户数据完全隔离
- ✅ Cookie 和登录状态按用户存储
- ✅ 删除操作验证所有权
---
## 🌐 Storage URL 修复 (16:00)
### 问题描述
生成的视频 URL 为 `http://localhost:8008/...`,前端无法访问。
### 解决方案
#### 1. 后端配置 (`backend/.env`)
```ini
SUPABASE_URL=http://localhost:8008 # 内部访问
SUPABASE_PUBLIC_URL=https://api.hbyrkj.top # 公网访问
```
#### 2. URL 转换 (`backend/app/services/storage.py`)
```python
def _convert_to_public_url(self, url: str) -> str:
"""将内部 URL 转换为公网可访问的 URL"""
if settings.SUPABASE_PUBLIC_URL and settings.SUPABASE_URL:
internal_url = settings.SUPABASE_URL.rstrip('/')
public_url = settings.SUPABASE_PUBLIC_URL.rstrip('/')
return url.replace(internal_url, public_url)
return url
```
### 结果
- ✅ 前端获取的 URL 可正常访问
- ✅ 视频预览和下载功能正常
---
## ⚡ 发布服务优化 - 本地文件直读 (16:30)
### 问题描述
发布视频时需要先通过 HTTP 下载 Supabase Storage 文件到临时目录,效率低且浪费资源。
### 发现
Supabase Storage 文件实际存储在本地磁盘:
```
/home/rongye/ProgramFiles/Supabase/volumes/storage/stub/stub/{bucket}/{path}/{internal_uuid}
```
### 解决方案
#### 1. 添加本地路径获取方法 (`storage.py`)
```python
SUPABASE_STORAGE_LOCAL_PATH = Path("/home/rongye/ProgramFiles/Supabase/volumes/storage/stub/stub")
def get_local_file_path(self, bucket: str, path: str) -> Optional[str]:
"""获取 Storage 文件的本地磁盘路径"""
dir_path = SUPABASE_STORAGE_LOCAL_PATH / bucket / path
if not dir_path.exists():
return None
files = list(dir_path.iterdir())
return str(files[0]) if files else None
```
#### 2. 发布服务优先使用本地文件 (`publish_service.py`)
```python
# 解析 URL 获取 bucket 和 path
match = re.search(r'/storage/v1/object/sign/([^/]+)/(.+?)\?', video_path)
if match:
bucket, storage_path = match.group(1), match.group(2)
local_video_path = storage_service.get_local_file_path(bucket, storage_path)
if local_video_path and os.path.exists(local_video_path):
logger.info(f"[发布] 直接使用本地文件: {local_video_path}")
else:
# Fallback: HTTP 下载
```
### 结果
- ✅ 发布速度显著提升(跳过下载步骤)
- ✅ 减少临时文件占用
- ✅ 保留 HTTP 下载作为 Fallback
---
## 🔧 Supabase Studio 配置 (17:00)
### 修改内容
更新 `/home/rongye/ProgramFiles/Supabase/.env`
```ini
# 修改前
SUPABASE_PUBLIC_URL=http://localhost:8000
# 修改后
SUPABASE_PUBLIC_URL=https://api.hbyrkj.top
```
### 原因
通过 `supabase.hbyrkj.top` 公网访问 Studio 时,需要正确的 API 公网地址。
### 操作
```bash
docker compose restart studio
```
### 待解决
- 🔄 Studio Settings 页面加载问题401 Unauthorized- 可能与 Nginx Basic Auth 配置冲突
---
## 📁 今日修改文件清单
| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `backend/app/api/materials.py` | 修改 | 添加用户隔离 |
| `backend/app/api/videos.py` | 修改 | 添加用户隔离 |
| `backend/app/services/storage.py` | 修改 | URL转换 + 本地路径获取 |
| `backend/app/services/publish_service.py` | 修改 | 本地文件直读优化 |
| `backend/.env` | 修改 | 添加 SUPABASE_PUBLIC_URL |
| `Supabase/.env` | 修改 | SUPABASE_PUBLIC_URL |
| `frontend/src/app/page.tsx` | 修改 | 改用后端API上传 |
---
## 📅 明日任务规划 (Day 12)
### 🎯 目标:部署 Qwen3-TTS 0.6B 声音克隆系统
**任务背景**
- 当前使用 EdgeTTS微软云端 TTS音色固定无法自定义
- Qwen3-TTS 支持**零样本声音克隆**,可用少量音频克隆任意人声
**核心任务**
1. **模型部署**
- 创建独立 Conda 环境 (`qwen-tts`)
- 下载 Qwen3-TTS 0.6B 模型权重
- 配置 GPU 推理环境
2. **后端集成**
- 新增 `qwen_tts_service.py` 服务
- 支持声音克隆:上传参考音频 → 生成克隆语音
- 兼容现有 `tts_service.py` 接口
3. **前端适配**
- 添加"声音克隆"选项
- 支持上传参考音频3-10秒
- 音色预览功能
**预期成果**
- ✅ 用户可上传自己的声音样本
- ✅ 生成的口播视频使用克隆后的声音
- ✅ 保留 EdgeTTS 作为备选方案
**参考资源**
- 模型:[Qwen/Qwen3-TTS-0.6B](https://huggingface.co/Qwen/Qwen3-TTS-0.6B)
- 显存需求:~4GB (0.6B 参数量)