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="找不到该内容记录")