Files
ViGent2/Docs/DevLogs/Day11.md
Kevin Wong ee8cb9cfd2 更新
2026-01-27 16:52:40 +08:00

7.7 KiB
Raw Blame History

## 🔧 上传架构重构 (Direct Upload) ### 🚨 问题描述 (10:30) 现象:上传大于 7MB 的文件时,后端返回 500 Internal Server Error实际为 ClientDisconnectROOT 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 桶的 INSERTSELECT 权限。 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
    cat supabase_rls.sql | docker exec -i supabase-db psql -U postgres
    
  3. 验证前端上传成功。

🔐 用户隔离实现 (15:00)

问题描述

不同账户登录后能看到其他用户上传的素材和生成的视频,缺乏数据隔离。

解决方案:存储路径前缀隔离

1. 素材模块 (backend/app/api/materials.py)

# 上传时添加用户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)

# 生成视频时使用用户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)

SUPABASE_URL=http://localhost:8008        # 内部访问
SUPABASE_PUBLIC_URL=https://api.hbyrkj.top  # 公网访问

2. URL 转换 (backend/app/services/storage.py)

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)

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)

# 解析 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

# 修改前
SUPABASE_PUBLIC_URL=http://localhost:8000

# 修改后
SUPABASE_PUBLIC_URL=https://api.hbyrkj.top

原因

通过 supabase.hbyrkj.top 公网访问 Studio 时,需要正确的 API 公网地址。

操作

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上传