File size: 6,428 Bytes
133609a
8d7d15e
133609a
 
8d7d15e
133609a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8d7d15e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133609a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from fastapi import APIRouter, File, UploadFile, HTTPException, status, Depends, Query
from fastapi.responses import FileResponse # Import FileResponse
from typing import List, Optional
import os
from pathlib import Path # Import Path
from supabase import create_client, Client
from gotrue.errors import AuthApiError
from pydantic import BaseModel # Import BaseModel for UserListResponse

from core.config import SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY
from core.dependencies import get_current_admin_user, get_supabase_client
from core.models import User, AdminUser, UserUpdate
from core.utils import get_password_hash

router = APIRouter()

UPLOAD_DIRECTORY = "static/images"

@router.post("/upload-image")
async def upload_image(file: UploadFile = File(...), current_user: User = Depends(get_current_admin_user)):
    """
    上传图片到 /static/images 目录。
    """
    if not os.path.exists(UPLOAD_DIRECTORY):
        os.makedirs(UPLOAD_DIRECTORY)

    file_location = os.path.join(UPLOAD_DIRECTORY, file.filename)
    try:
        with open(file_location, "wb+") as file_object:
            file_object.write(await file.read())
        return {"filename": file.filename, "path": f"/{UPLOAD_DIRECTORY}/{file.filename}", "message": "图片上传成功"}
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"图片上传失败: {e}")

@router.get("/images")
async def list_images(current_user: User = Depends(get_current_admin_user)):
    """
    列出 /static/images 目录下的所有图片。
    """
    if not os.path.exists(UPLOAD_DIRECTORY):
        return []
    
    image_files = []
    for filename in os.listdir(UPLOAD_DIRECTORY):
        if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.svg')):
            image_files.append({
                "filename": filename,
                "path": f"/{UPLOAD_DIRECTORY}/{filename}"
            })
    return image_files

@router.delete("/images/{filename}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_image(filename: str, current_user: User = Depends(get_current_admin_user)):
    """
    删除 /static/images 目录下的指定图片。
    """
    file_path = Path(UPLOAD_DIRECTORY) / filename
    if not file_path.is_file():
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="图片未找到")
    
    try:
        os.remove(file_path)
        return # No content for 204
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"图片删除失败: {e}")

class UserListResponse(BaseModel):
    users: List[AdminUser]
    total_count: int

@router.get("/users", response_model=UserListResponse) # Update response_model
async def get_all_users(
    current_user: User = Depends(get_current_admin_user),
    supabase_client: Client = Depends(get_supabase_client),
    page: int = Query(1, ge=1),
    page_size: int = Query(10, ge=1, le=100),
    search: Optional[str] = Query(None)
):
    """
    获取所有用户列表(仅限管理员)。
    """
    offset = (page - 1) * page_size
    query = supabase_client.table('sp_users').select('id, email, email_verified, created_at, is_admin, disabled', count='exact') # Include disabled field
    
    if search:
        query = query.ilike('email', f"%{search}%") # Case-insensitive search by email

    res = query.order('created_at', desc=True).range(offset, offset + page_size - 1).execute()
    
    users = [AdminUser(**user) for user in res.data]
    total_count = res.count
    
    return {"users": users, "total_count": total_count}

@router.get("/users/{user_id}", response_model=AdminUser)
async def get_user_by_id(
    user_id: str,
    current_user: User = Depends(get_current_admin_user),
    supabase_client: Client = Depends(get_supabase_client)
):
    """
    根据用户ID获取单个用户信息(仅限管理员)。
    """
    res = supabase_client.table('sp_users').select('id, email, email_verified, created_at, is_admin, disabled').eq('id', user_id).single().execute() # Include disabled field
    if not res.data:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户未找到")
    return AdminUser(**res.data)

@router.put("/users/{user_id}", response_model=AdminUser)
async def update_user(
    user_id: str,
    user_update: UserUpdate,
    current_user: User = Depends(get_current_admin_user),
    supabase_client: Client = Depends(get_supabase_client)
):
    """
    更新用户信息(仅限管理员)。
    """
    update_data = user_update.dict(exclude_unset=True)
    
    if 'password' in update_data and update_data['password']:
        update_data['password_hash'] = get_password_hash(update_data['password'])
        del update_data['password'] # Remove plain password from update_data

    # Handle disabled field update
    if 'disabled' in update_data and update_data['disabled'] is not None:
        # Supabase update for 'disabled' field
        pass # The update_data dictionary already contains 'disabled' if it was set

    if not update_data:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="没有提供更新数据")

    res = supabase_client.table('sp_users').update(update_data).eq('id', user_id).execute()
    
    if not res.data:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户未找到或更新失败")
    
    # Fetch the updated user to return
    updated_user_res = supabase_client.table('sp_users').select('id, email, email_verified, created_at, is_admin, disabled').eq('id', user_id).single().execute() # Include disabled field
    return AdminUser(**updated_user_res.data)

@router.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(
    user_id: str,
    current_user: User = Depends(get_current_admin_user),
    supabase_client: Client = Depends(get_supabase_client)
):
    """
    删除用户(仅限管理员)。
    """
    # First, delete associated API keys
    supabase_client.table('sp_user_api_keys').delete().eq('user_id', user_id).execute()

    # Then delete the user from sp_users table
    res = supabase_client.table('sp_users').delete().eq('id', user_id).execute()
    
    if not res.data:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户未找到或删除失败")
    return