Spaces:
Running
Running
File size: 8,777 Bytes
088dffd 71ce6d2 088dffd 7b74f04 088dffd 71ce6d2 088dffd 13880c6 088dffd e87b2e5 088dffd 7b74f04 e87b2e5 7b74f04 088dffd e87b2e5 4c9deec 088dffd e87b2e5 088dffd 13880c6 088dffd 13880c6 088dffd 13880c6 e87b2e5 13880c6 7b74f04 13880c6 7b74f04 13880c6 7b74f04 13880c6 7b74f04 13880c6 088dffd 13880c6 7b74f04 088dffd 13880c6 7b74f04 e87b2e5 13880c6 088dffd 13880c6 088dffd e87b2e5 088dffd e87b2e5 85494ee 088dffd 4c9deec 13880c6 088dffd 71ce6d2 e87b2e5 71ce6d2 13880c6 e87b2e5 71ce6d2 4c9deec e87b2e5 71ce6d2 e87b2e5 71ce6d2 e87b2e5 13880c6 71ce6d2 e87b2e5 ea92de7 13880c6 ea92de7 13880c6 ea92de7 e87b2e5 ea92de7 | 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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | # router_items.py
from fastapi import APIRouter, HTTPException
import time
import uuid
import datetime
import 数据库连接 as db
from models import ItemCreate, ItemUpdate
router = APIRouter()
def get_last_6_months():
res = []
today = datetime.date.today()
for i in range(5, -1, -1):
m = today.month - i
y = today.year
while m <= 0:
m += 12
y -= 1
res.append(f"{y}-{m:02d}")
return res
@router.get("/api/items")
async def get_items(type: str = "tool", sort: str = "time", limit: int = 50): # 优化:默认限制调大至 50,提升前端列表体验
items_db = db.load_data("items.json", default_data=[])
comments_db = db.load_data("comments.json", default_data={})
# 如果是推荐榜,匹配所有 recommend 开头的子类型
if type == "recommend":
filtered_items = [item for item in items_db if item.get("type", "").startswith("recommend")]
else:
filtered_items = [item for item in items_db if item.get("type") == type]
for item in filtered_items:
item["commentsData"] = comments_db.get(item["id"], [])
item["comments"] = len(item["commentsData"])
# 🔴 【绝对核心防线】:在下发给前端前,强行在内存中抹除创作者的 Token!
# 这样即使资源是公开展示的,普通用户也绝对抓不到源仓库的密钥。
item.pop("github_token", None)
if sort == "likes": filtered_items.sort(key=lambda x: x.get("likes", 0), reverse=True)
elif sort == "favorites": filtered_items.sort(key=lambda x: x.get("favorites", 0), reverse=True)
elif sort == "downloads": filtered_items.sort(key=lambda x: x.get("uses", 0), reverse=True)
else: filtered_items.sort(key=lambda x: x.get("created_at", 0), reverse=True)
return {"status": "success", "data": filtered_items[:limit]}
@router.get("/api/creators")
async def get_creators(sort: str = "downloads", limit: int = 20):
users_db = db.load_data("users.json", default_data={})
items_db = db.load_data("items.json", default_data=[])
comments_db = db.load_data("comments.json", default_data={})
creators = []
months = get_last_6_months()
for account, u in users_db.items():
u_items = [i for i in items_db if i.get("author") == account]
trend_tools = {m: 0 for m in months}
trend_apps = {m: 0 for m in months}
trend_recommends = {m: 0 for m in months}
tools_count = 0
apps_count = 0
for i in u_items:
itype = i.get("type", "")
history = i.get("use_history", {})
if itype == "tool" or itype == "recommend_tool":
if itype == "tool": tools_count += 1
for m in months: trend_tools[m] += history.get(m, 0)
elif itype == "app" or itype == "recommend_app":
if itype == "app": apps_count += 1
for m in months: trend_apps[m] += history.get(m, 0)
elif itype.startswith("recommend"):
for m in months: trend_recommends[m] += history.get(m, 0)
creators.append({
"account": account, "name": u.get("name", account), "avatar": u.get("avatarDataUrl", "https://via.placeholder.com/150"),
"shortDesc": u.get("intro", "这个人很懒,什么都没写..."), "fullDesc": u.get("intro", "这个人很懒,什么都没写..."),
"likes": sum(i.get("likes", 0) for i in u_items), "favorites": sum(i.get("favorites", 0) for i in u_items),
"downloads": sum(i.get("uses", 0) for i in u_items),
"toolsCount": tools_count, "appsCount": apps_count, "followers": len(u.get("followers", [])), "created_at": u.get("created_at", 0),
"commentsData": comments_db.get(account, []),
"trendData": {
"months": months,
"tools": [trend_tools[m] for m in months],
"apps": [trend_apps[m] for m in months],
"recommends": [trend_recommends[m] for m in months]
}
})
if sort == "likes": creators.sort(key=lambda x: x.get("likes", 0), reverse=True)
elif sort == "favorites": creators.sort(key=lambda x: x.get("favorites", 0), reverse=True)
elif sort == "downloads": creators.sort(key=lambda x: x.get("downloads", 0), reverse=True)
else: creators.sort(key=lambda x: x.get("created_at", 0), reverse=True)
return {"status": "success", "data": creators[:limit]}
@router.post("/api/items")
async def create_item(item: ItemCreate):
# 【安全加固】:强制转换为整数,并拦截负数 (防浮点漏洞与洗钱)
item.price = int(item.price)
if item.price < 0:
raise HTTPException(status_code=400, detail="🚨 安全拦截:商品价格不能为负数")
items_db = db.load_data("items.json", default_data=[])
new_item = {
"id": f"{item.type}_{int(time.time())}_{uuid.uuid4().hex[:6]}", "type": item.type, "title": item.title, "author": item.author,
"shortDesc": item.shortDesc, "fullDesc": item.fullDesc, "link": item.link, "coverUrl": item.coverUrl, "price": item.price,
"github_token": item.github_token, # 【新增】保存密钥到云端 JSON
"likes": 0, "favorites": 0, "comments": 0, "uses": 0, "use_history": {}, "created_at": int(time.time()), "liked_by": [], "favorited_by": []
}
items_db.insert(0, new_item)
db.save_data("items.json", items_db)
return {"status": "success", "data": new_item}
@router.put("/api/items/{item_id}")
async def update_item(item_id: str, update_data: ItemUpdate, author: str):
# 【安全加固】:更新时同样强制转换为整数并拦截负数
if update_data.price is not None:
update_data.price = int(update_data.price)
if update_data.price < 0:
raise HTTPException(status_code=400, detail="🚨 安全拦截:商品价格不能为负数")
items_db = db.load_data("items.json", default_data=[])
for item in items_db:
if item["id"] == item_id:
if item.get("author") != author: raise HTTPException(status_code=403, detail="无权修改他人发布的内容")
if update_data.title is not None: item["title"] = update_data.title
if update_data.shortDesc is not None: item["shortDesc"] = update_data.shortDesc
if update_data.fullDesc is not None: item["fullDesc"] = update_data.fullDesc
if update_data.link is not None: item["link"] = update_data.link
if update_data.coverUrl is not None: item["coverUrl"] = update_data.coverUrl
if update_data.price is not None: item["price"] = update_data.price
if update_data.github_token is not None: item["github_token"] = update_data.github_token # 【新增】允许更新密钥
db.save_data("items.json", items_db)
return {"status": "success"}
raise HTTPException(status_code=404, detail="找不到该内容记录")
@router.delete("/api/items/{item_id}")
async def delete_item(item_id: str, author: str):
items_db = db.load_data("items.json", default_data=[])
target_idx = next((i for i, item in enumerate(items_db) if item["id"] == item_id), None)
if target_idx is None: raise HTTPException(status_code=404, detail="找不到该内容记录")
if items_db[target_idx].get("author") != author: raise HTTPException(status_code=403, detail="无权删除他人发布的内容")
items_db.pop(target_idx)
db.save_data("items.json", items_db)
comments_db = db.load_data("comments.json", default_data={})
if item_id in comments_db:
del comments_db[item_id]
db.save_data("comments.json", comments_db)
return {"status": "success"}
@router.post("/api/items/{item_id}/use")
async def record_item_use(item_id: str):
items_db = db.load_data("items.json", default_data=[])
current_month = datetime.date.today().strftime("%Y-%m")
for item in items_db:
if item["id"] == item_id:
item["uses"] = item.get("uses", 0) + 1
if "use_history" not in item:
item["use_history"] = {}
item["use_history"][current_month] = item["use_history"].get(current_month, 0) + 1
db.save_data("items.json", items_db)
return {"status": "success", "uses": item["uses"]}
raise HTTPException(status_code=404, detail="找不到该内容记录") |