# ⚙️ 后端逻辑/核心服务端.py (Hugging Face Spaces app.py) from fastapi import FastAPI, File, UploadFile, Form from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import Response, JSONResponse from pydantic import BaseModel # 【新增】:引入标准数据模型 import hashlib import urllib.parse import urllib.request import os import 数据库连接 as db # 引入拆分后的四大业务模块 from router_users import router as users_router from router_items import router as items_router from router_comments import router as comments_router from router_messages import router as messages_router app = FastAPI(title="ComfyUI Ranking Community API") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(users_router) app.include_router(items_router) app.include_router(comments_router) app.include_router(messages_router) @app.get("/") def read_root(): return {"status": "ok", "message": "API is running and fully modularized!"} @app.post("/api/upload") async def upload_file(file: UploadFile = File(...), file_type: str = Form(...)): content = await file.read() file_hash = hashlib.md5(content).hexdigest()[:10] new_filename = f"{file_hash}_{file.filename}" safe_filename = urllib.parse.quote(file.filename) safe_url_filename = f"{file_hash}_{safe_filename}" dir_mapping = {"avatar": "avatars", "cover": "covers", "tool": "tools", "app": "apps"} target_dir = dir_mapping.get(file_type, "others") full_path_in_repo = f"{target_dir}/{new_filename}" db.save_file(full_path_in_repo, content) url = f"https://huggingface.co/datasets/{db.DATASET_REPO_ID}/resolve/main/{target_dir}/{safe_url_filename}" return {"status": "success", "url": url, "display_name": file.filename, "hashed_name": new_filename} # ========================================================= # 【核心修复】:使用 huggingface_hub 官方库替代 urllib 解决重定向 401 问题 # ========================================================= from huggingface_hub import hf_hub_download class ProxyDownloadRequest(BaseModel): url: str @app.post("/api/proxy_download") async def proxy_download(req_data: ProxyDownloadRequest): target_url = req_data.url # 校验 URL 格式 if not target_url or "resolve/main/" not in target_url: return JSONResponse(content={"error": "无效的 Hugging Face 下载链接"}, status_code=400) hf_token = os.environ.get("HF_TOKEN") if not hf_token: return JSONResponse(content={"error": "云端环境变量未配置 HF_TOKEN,无法读取私有库"}, status_code=401) try: # 1. 从前端传来的 URL 中切割出仓库内的真实相对路径 # 例如从 https://.../resolve/main/apps/123_%E6%B5%8B.json 提取出 apps/123_%E6%B5%8B.json repo_path_encoded = target_url.split("resolve/main/")[-1] # 2. 对可能存在的被编码的中文字符进行解码 (恢复为 apps/123_测.json) repo_path = urllib.parse.unquote(repo_path_encoded) # 3. 调用官方库直接从私有 Dataset 拉取文件(完美处理鉴权和重定向) cached_file_path = hf_hub_download( repo_id=db.DATASET_REPO_ID, repo_type="dataset", filename=repo_path, token=hf_token ) # 4. 读取缓存在云端的本地文件流并返回给前端 with open(cached_file_path, "rb") as f: content = f.read() return Response(content=content, media_type="application/json") except Exception as e: return JSONResponse(content={"error": f"云端官方库读取失败: {str(e)}"}, status_code=500)