File size: 5,249 Bytes
f68de17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d85da6
 
f68de17
 
6f7ceef
f68de17
4c9deec
 
 
 
 
 
 
f68de17
 
 
 
4c9deec
 
f68de17
 
 
 
 
 
 
 
 
 
 
3ff2ccf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# router_proxy.py
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import StreamingResponse, JSONResponse
from sqlalchemy.orm import Session
from pydantic import BaseModel
import httpx
import os
import 数据库连接 as json_db
from database_sql import get_db
from models_sql import Ownership

router = APIRouter()

class ProxyGithubZipRequest(BaseModel):
    url: str
    item_id: str
    account: str

@router.post("/api/proxy_github_zip")
async def proxy_github_zip(req_data: ProxyGithubZipRequest, db: Session = Depends(get_db)):
    """云端代理:校验所有权后,拉取 GitHub 私有库 ZIP 流透传给本地"""
    items_db = json_db.load_data("items.json", default_data=[])
    item = next((i for i in items_db if i["id"] == req_data.item_id), None)
    if not item: return JSONResponse(content={"error": "资源不存在"}, status_code=404)
    
    # 1. 核心鉴权:验证用户是否购买过该工具
    price = int(item.get("price", 0))
    if price > 0 and req_data.account != item.get("author"):
        owned = db.query(Ownership).filter(Ownership.account == req_data.account, Ownership.item_id == req_data.item_id).first()
        if not owned:
            return JSONResponse(content={"error": "🚨 拒绝访问:未找到购买记录!"}, status_code=403)

    # 2. 解析 GitHub 仓库信息
    repo_url = item.get("link", "").rstrip("/")
    if not repo_url.startswith("https://github.com/"):
        return JSONResponse(content={"error": "无效的仓库地址,目前仅支持 GitHub 私有库代理"}, status_code=400)

    repo_parts = repo_url.split("/")
    if len(repo_parts) < 2: return JSONResponse(content={"error": "无效的仓库地址格式"}, status_code=400)
    owner = repo_parts[-2]
    repo = repo_parts[-1].replace(".git", "")
    
    # GitHub 官方提供的打包下载 API
    github_zip_api = f"https://api.github.com/repos/{owner}/{repo}/zipball"
    
    # 【核心修改】:优先读取该资源在数据库中绑定的专属创作者 Token
    creator_token = item.get("github_token")
    # 如果没填,尝试使用官方全局兜底的 PAT
    fallback_token = os.environ.get("GITHUB_PAT") 
    
    active_token = creator_token if creator_token else fallback_token

    headers = {
        "Accept": "application/vnd.github.v3+json",
        "User-Agent": "ComfyUI-Ranking-SaaS"
    }
    if active_token:
        headers["Authorization"] = f"Bearer {active_token}"

    # 3. 异步请求 GitHub API 并以流形式透传回客户端 (防内存打爆)
    async def stream_generator():
        async with httpx.AsyncClient(follow_redirects=True) as client:
            async with client.stream("GET", github_zip_api, headers=headers) as response:
                if response.status_code != 200:
                    yield b"GITHUB_DOWNLOAD_FAILED"
                    return
                async for chunk in response.aiter_bytes():
                    yield chunk

    return StreamingResponse(stream_generator(), media_type="application/zip")

# ==========================================
# 新增:工作流/应用 (App) JSON 代理下载接口
# ==========================================
class ProxyDownloadRequest(BaseModel):
    url: str
    item_id: str
    account: str

@router.post("/api/proxy_download")
async def proxy_download(req_data: ProxyDownloadRequest, db: Session = Depends(get_db)):
    """云端代理:校验所有权后,拉取真实的工作流 JSON 文件并透传给本地"""
    items_db = json_db.load_data("items.json", default_data=[])
    item = next((i for i in items_db if i["id"] == req_data.item_id), None)
    
    if not item: 
        return JSONResponse(content={"error": "云端记录中找不到该资源"}, status_code=404)
    
    # 1. 核心鉴权:验证用户是否购买/获取过该工作流
    price = int(item.get("price", 0))
    if price > 0 and req_data.account != item.get("author"):
        owned = db.query(Ownership).filter(Ownership.account == req_data.account, Ownership.item_id == req_data.item_id).first()
        if not owned:
            return JSONResponse(content={"error": "您尚未获取该工作流,请先在社区页面点击获取"}, status_code=403)

    target_url = req_data.url
    
    # 2. 异步拉取真实 JSON 数据并透传回客户端
    try:
        # 开启 follow_redirects=True 防止源地址有 302 重定向跳转
        async with httpx.AsyncClient(follow_redirects=True) as client:
            resp = await client.get(target_url, timeout=30.0)
            
            if resp.status_code != 200:
                return JSONResponse(content={"error": f"源文件拉取失败,HTTP状态码: {resp.status_code}"}, status_code=resp.status_code)
            
            # 成功则直接将 JSON 二进制流原样返回给本地 ComfyUI 引擎
            from fastapi.responses import Response
            return Response(content=resp.content, media_type="application/json")
            
    except Exception as e:
        return JSONResponse(content={"error": f"代理下载时发生网络异常: {str(e)}"}, status_code=500)