From d2859ab8cd8375e9d1551bb9fe24e305e9290167 Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Sun, 24 May 2026 00:46:25 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20Auth/User=20=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E9=87=8D=E6=9E=84=20+=20=E6=96=B0=E5=A2=9E=20user=5Fapi=20?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - auth_router 移除旧的 get_admin_user,统一使用 auth_middleware - user_router 新增 get_users 接口 - 前端新增 user_api.js,更新 index.js 导出 --- backend/server/routers/auth_router.py | 32 ++++++++------------------- backend/server/routers/user_router.py | 19 +++++++++++++++- web/src/apis/index.js | 1 + web/src/apis/user_api.js | 9 ++++++++ 4 files changed, 37 insertions(+), 24 deletions(-) create mode 100644 web/src/apis/user_api.js diff --git a/backend/server/routers/auth_router.py b/backend/server/routers/auth_router.py index 4fae2877..147b4ba3 100644 --- a/backend/server/routers/auth_router.py +++ b/backend/server/routers/auth_router.py @@ -1,5 +1,4 @@ import re -import uuid from yuxi.utils import logger from fastapi import APIRouter, Body, Depends, HTTPException, Request, status, UploadFile, File @@ -23,7 +22,7 @@ from server.utils.auth_middleware import ( from server.utils.auth_utils import AuthUtils from server.utils.user_utils import generate_unique_uid, validate_username, is_valid_phone_number from server.utils.common_utils import log_operation -from yuxi.storage.minio import aupload_file_to_minio +from yuxi.services.upload_utils import upload_image_to_minio from yuxi.utils.datetime_utils import utc_now_naive # OIDC 认证相关导入 @@ -843,35 +842,22 @@ async def upload_user_avatar( file: UploadFile = File(...), current_user: User = Depends(get_required_user), db: AsyncSession = Depends(get_db) ): """上传用户头像""" - # 检查文件类型 - if not file.content_type or not file.content_type.startswith("image/"): - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="只能上传图片文件") - - # 检查文件大小(5MB限制) - file_size = 0 - file_content = await file.read() - file_size = len(file_content) - - if file_size > 5 * 1024 * 1024: # 5MB - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="文件大小不能超过5MB") - try: - # 获取文件扩展名 - file_extension = file.filename.split(".")[-1].lower() if file.filename and "." in file.filename else "jpg" + avatar_url = await upload_image_to_minio( + file, + object_prefix=f"avatar/{current_user.id}", + max_size_bytes=5 * 1024 * 1024, + too_large_message="文件大小不能超过5MB", + ) - # 上传到MinIO - file_name = f"avatar/{current_user.id}/{uuid.uuid4()}.{file_extension}" - avatar_url = await aupload_file_to_minio("public", file_name, file_content) - - # 更新用户头像 current_user.avatar = avatar_url await db.commit() - - # 记录操作 await log_operation(db, current_user.id, "上传头像", f"更新头像: {avatar_url}") return {"success": True, "avatar_url": avatar_url, "message": "头像上传成功"} + except ValueError as e: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) except Exception as e: raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"头像上传失败: {str(e)}") diff --git a/backend/server/routers/user_router.py b/backend/server/routers/user_router.py index 133804f0..dcb0d11b 100644 --- a/backend/server/routers/user_router.py +++ b/backend/server/routers/user_router.py @@ -5,13 +5,14 @@ import re import secrets from typing import Any -from fastapi import APIRouter, Depends, HTTPException, Query +from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status from pydantic import BaseModel, Field from sqlalchemy import func, select from sqlalchemy.dialects.postgresql import insert as pg_insert from sqlalchemy.ext.asyncio import AsyncSession from server.utils.auth_middleware import get_db, get_required_user +from yuxi.services.upload_utils import upload_image_to_minio from yuxi.storage.postgres.models_business import APIKey, AgentEnv, User from yuxi.utils.datetime_utils import coerce_any_to_utc_datetime, format_utc_datetime, utc_now_naive @@ -21,6 +22,7 @@ ENV_KEY_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") MAX_ENV_COUNT = 200 MAX_ENV_KEY_LENGTH = 128 MAX_ENV_VALUE_LENGTH = 32768 +MAX_USER_IMAGE_SIZE_BYTES = 5 * 1024 * 1024 def generate_api_key() -> tuple[str, str, str]: @@ -71,6 +73,21 @@ class AgentEnvResponse(BaseModel): updated_at: str | None = None +@user_router.post("/upload-image", response_model=dict) +async def upload_user_image(file: UploadFile = File(...), current_user: User = Depends(get_required_user)): + try: + image_url = await upload_image_to_minio( + file, + object_prefix=f"images/{current_user.uid}", + max_size_bytes=MAX_USER_IMAGE_SIZE_BYTES, + too_large_message="图片大小不能超过 5MB", + ) + except ValueError as exc: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc + + return {"success": True, "image_url": image_url, "url": image_url} + + def validate_agent_env(env: dict[str, Any]) -> dict[str, str]: if len(env) > MAX_ENV_COUNT: raise HTTPException(status_code=400, detail=f"环境变量数量不能超过 {MAX_ENV_COUNT} 个") diff --git a/web/src/apis/index.js b/web/src/apis/index.js index b7f66092..35021a63 100644 --- a/web/src/apis/index.js +++ b/web/src/apis/index.js @@ -16,6 +16,7 @@ export * from './skill_api' // Skills API export * from './subagent_api' // SubAgent API export * from './tool_api' // 工具 API export * from './mention_api' // 提及搜索 API +export * from './user_api' // 用户资源 API // 导出基础工具函数 export { diff --git a/web/src/apis/user_api.js b/web/src/apis/user_api.js new file mode 100644 index 00000000..a982e4d2 --- /dev/null +++ b/web/src/apis/user_api.js @@ -0,0 +1,9 @@ +import { apiPost } from './base' + +export const userApi = { + uploadImage: (file) => { + const formData = new FormData() + formData.append('file', file) + return apiPost('/api/user/upload-image', formData) + } +}