mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 20:55:14 +00:00
refactor: 大规模代码整理与功能优化
1. 重构后端API路由、CRUD与模块结构,整合日志管理,移除废弃demo代码 2. 优化前端组件类型定义、样式与路由配置,修复权限判断逻辑 3. 调整默认排序规则、滚动条样式与工具类函数,更新依赖与配置文件 4. 修复多处类型不匹配与默认值问题,完善表单与菜单验证逻辑
This commit is contained in:
+1
-16
@@ -8,19 +8,17 @@ from app.api.v1.module_monitor.cache.schema import CacheInfoSchema, CacheMonitor
|
||||
from app.common.response import ResponseSchema, SuccessResponse
|
||||
from app.core.dependencies import AuthPermission, redis_getter
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import log
|
||||
from app.core.router_class import OperationLogRoute
|
||||
|
||||
from .service import CacheService
|
||||
|
||||
CacheRouter = APIRouter(route_class=OperationLogRoute, prefix="/cache", tags=["缓存监控"])
|
||||
CacheRouter = APIRouter(route_class=OperationLogRoute, prefix="/cache", tags=["系统监控/缓存监控"])
|
||||
|
||||
|
||||
@CacheRouter.get(
|
||||
"/info",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:cache:query"]))],
|
||||
summary="获取缓存监控信息",
|
||||
description="获取缓存监控信息",
|
||||
response_model=ResponseSchema[CacheMonitorSchema],
|
||||
)
|
||||
async def get_monitor_cache_info_controller(
|
||||
@@ -36,7 +34,6 @@ async def get_monitor_cache_info_controller(
|
||||
- JSONResponse: 包含缓存监控统计信息的JSON响应
|
||||
"""
|
||||
result = await CacheService.get_cache_monitor_statistical_info_service(redis=redis)
|
||||
log.info("获取缓存监控信息成功")
|
||||
return SuccessResponse(data=result, msg="获取缓存监控信息成功")
|
||||
|
||||
|
||||
@@ -44,7 +41,6 @@ async def get_monitor_cache_info_controller(
|
||||
"/get/names",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:cache:query"]))],
|
||||
summary="获取缓存名称列表",
|
||||
description="获取缓存名称列表",
|
||||
response_model=ResponseSchema[list[CacheInfoSchema]],
|
||||
)
|
||||
async def get_monitor_cache_name_controller() -> JSONResponse:
|
||||
@@ -55,7 +51,6 @@ async def get_monitor_cache_name_controller() -> JSONResponse:
|
||||
- JSONResponse: 包含缓存名称列表的JSON响应
|
||||
"""
|
||||
result = await CacheService.get_cache_monitor_cache_name_service()
|
||||
log.info("获取缓存名称列表成功")
|
||||
return SuccessResponse(data=result, msg="获取缓存名称列表成功")
|
||||
|
||||
|
||||
@@ -63,7 +58,6 @@ async def get_monitor_cache_name_controller() -> JSONResponse:
|
||||
"/get/keys/{cache_name}",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:cache:query"]))],
|
||||
summary="获取缓存键名列表",
|
||||
description="获取缓存键名列表",
|
||||
response_model=ResponseSchema[list[CacheInfoSchema]],
|
||||
)
|
||||
async def get_monitor_cache_key_controller(
|
||||
@@ -81,7 +75,6 @@ async def get_monitor_cache_key_controller(
|
||||
result = await CacheService.get_cache_monitor_cache_key_service(
|
||||
redis=redis, cache_name=cache_name
|
||||
)
|
||||
log.info(f"获取缓存{cache_name}的键名列表成功")
|
||||
return SuccessResponse(data=result, msg=f"获取缓存{cache_name}的键名列表成功")
|
||||
|
||||
|
||||
@@ -89,7 +82,6 @@ async def get_monitor_cache_key_controller(
|
||||
"/get/value/{cache_name}/{cache_key}",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:cache:query"]))],
|
||||
summary="获取缓存值",
|
||||
description="获取缓存值",
|
||||
response_model=ResponseSchema[CacheInfoSchema],
|
||||
)
|
||||
async def get_monitor_cache_value_controller(
|
||||
@@ -110,7 +102,6 @@ async def get_monitor_cache_value_controller(
|
||||
result = await CacheService.get_cache_monitor_cache_value_service(
|
||||
redis=redis, cache_name=cache_name, cache_key=cache_key
|
||||
)
|
||||
log.info(f"获取缓存{cache_name}:{cache_key}的值成功")
|
||||
return SuccessResponse(data=result, msg=f"获取缓存{cache_name}:{cache_key}的值成功")
|
||||
|
||||
|
||||
@@ -118,7 +109,6 @@ async def get_monitor_cache_value_controller(
|
||||
"/delete/name/{cache_name}",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:cache:delete"]))],
|
||||
summary="清除指定缓存名称的所有缓存",
|
||||
description="清除指定缓存名称的所有缓存",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def clear_monitor_cache_name_controller(
|
||||
@@ -138,7 +128,6 @@ async def clear_monitor_cache_name_controller(
|
||||
)
|
||||
if not result:
|
||||
raise CustomException(msg="清除缓存失败", data=result)
|
||||
log.info(f"清除缓存{cache_name}成功")
|
||||
return SuccessResponse(msg=f"{cache_name}对应键值清除成功", data=result)
|
||||
|
||||
|
||||
@@ -146,7 +135,6 @@ async def clear_monitor_cache_name_controller(
|
||||
"/delete/key/{cache_key}",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:cache:delete"]))],
|
||||
summary="清除指定缓存键",
|
||||
description="清除指定缓存键",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def clear_monitor_cache_key_controller(
|
||||
@@ -166,7 +154,6 @@ async def clear_monitor_cache_key_controller(
|
||||
)
|
||||
if not result:
|
||||
raise CustomException(msg="清除缓存失败", data=result)
|
||||
log.info(f"清除缓存键{cache_key}成功")
|
||||
return SuccessResponse(msg=f"{cache_key}清除成功", data=result)
|
||||
|
||||
|
||||
@@ -174,7 +161,6 @@ async def clear_monitor_cache_key_controller(
|
||||
"/delete/all",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:cache:delete"]))],
|
||||
summary="清除所有缓存",
|
||||
description="清除所有缓存",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def clear_monitor_cache_all_controller(
|
||||
@@ -192,5 +178,4 @@ async def clear_monitor_cache_all_controller(
|
||||
result = await CacheService.clear_cache_monitor_all_service(redis=redis)
|
||||
if not result:
|
||||
raise CustomException(msg="清除缓存失败", data=result)
|
||||
log.info("清除所有缓存成功")
|
||||
return SuccessResponse(msg="所有缓存清除成功", data=result)
|
||||
|
||||
+2
-10
@@ -1,13 +1,8 @@
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class CacheMonitorSchema(BaseModel):
|
||||
"""缓存监控信息模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
command_stats: list[dict] = Field(default_factory=list, description="Redis命令统计信息")
|
||||
db_size: int = Field(default=0, description="Redis数据库中的Key总数")
|
||||
info: dict = Field(default_factory=dict, description="Redis服务器信息")
|
||||
@@ -15,10 +10,7 @@ class CacheMonitorSchema(BaseModel):
|
||||
|
||||
class CacheInfoSchema(BaseModel):
|
||||
"""缓存对象信息模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
cache_key: str = Field(..., description="缓存键名")
|
||||
cache_name: str = Field(..., description="缓存名称")
|
||||
cache_value: Any = Field(default=None, description="缓存值")
|
||||
cache_value: str | None = Field(default=None, description="缓存值")
|
||||
remark: str | None = Field(default=None, description="备注说明")
|
||||
|
||||
+7
-11
@@ -12,7 +12,7 @@ class CacheService:
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def get_cache_monitor_statistical_info_service(cls, redis: Redis) -> dict:
|
||||
async def get_cache_monitor_statistical_info_service(cls, redis: Redis) -> CacheMonitorSchema:
|
||||
"""
|
||||
获取缓存监控信息。
|
||||
|
||||
@@ -30,30 +30,26 @@ class CacheService:
|
||||
{"name": key.split("_")[1], "value": str(value.get("calls"))}
|
||||
for key, value in command_stats_dict.items()
|
||||
]
|
||||
result = CacheMonitorSchema(command_stats=command_stats, db_size=db_size, info=info)
|
||||
|
||||
return result.model_dump()
|
||||
return CacheMonitorSchema(command_stats=command_stats, db_size=db_size, info=info)
|
||||
|
||||
@classmethod
|
||||
async def get_cache_monitor_cache_name_service(cls) -> list:
|
||||
async def get_cache_monitor_cache_name_service(cls) -> list[CacheInfoSchema]:
|
||||
"""
|
||||
获取缓存名称列表信息。
|
||||
|
||||
返回:
|
||||
- list: 缓存名称列表信息。
|
||||
"""
|
||||
name_list = [
|
||||
return [
|
||||
CacheInfoSchema(
|
||||
cache_key="",
|
||||
cache_name=key_config.key,
|
||||
cache_value="",
|
||||
remark=key_config.remark,
|
||||
).model_dump()
|
||||
)
|
||||
for key_config in RedisInitKeyConfig
|
||||
]
|
||||
|
||||
return name_list
|
||||
|
||||
@classmethod
|
||||
async def get_cache_monitor_cache_key_service(cls, redis: Redis, cache_name: str) -> list:
|
||||
"""
|
||||
@@ -76,7 +72,7 @@ class CacheService:
|
||||
@classmethod
|
||||
async def get_cache_monitor_cache_value_service(
|
||||
cls, redis: Redis, cache_name: str, cache_key: str
|
||||
) -> dict:
|
||||
) -> CacheInfoSchema:
|
||||
"""
|
||||
获取缓存内容信息。
|
||||
|
||||
@@ -95,7 +91,7 @@ class CacheService:
|
||||
cache_name=cache_name,
|
||||
cache_value=cache_value,
|
||||
remark="",
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def clear_cache_monitor_cache_name_service(cls, redis: Redis, cache_name: str) -> bool:
|
||||
|
||||
@@ -5,23 +5,21 @@ from fastapi.responses import JSONResponse
|
||||
from redis.asyncio.client import Redis
|
||||
|
||||
from app.common.request import PaginationService
|
||||
from app.common.response import ErrorResponse, ResponseSchema, SuccessResponse
|
||||
from app.common.response import ResponseSchema, SuccessResponse
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.dependencies import AuthPermission, redis_getter
|
||||
from app.core.logger import log
|
||||
from app.core.router_class import OperationLogRoute
|
||||
|
||||
from .schema import OnlineOutSchema, OnlineQueryParam
|
||||
from .service import OnlineService
|
||||
|
||||
OnlineRouter = APIRouter(route_class=OperationLogRoute, prefix="/online", tags=["在线用户"])
|
||||
OnlineRouter = APIRouter(route_class=OperationLogRoute, prefix="/online", tags=["系统监控/在线用户"])
|
||||
|
||||
|
||||
@OnlineRouter.get(
|
||||
"/list",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:online:query"]))],
|
||||
summary="获取在线用户列表",
|
||||
description="获取在线用户列表",
|
||||
response_model=ResponseSchema[list[OnlineOutSchema]],
|
||||
)
|
||||
async def get_online_list_controller(
|
||||
@@ -47,7 +45,6 @@ async def get_online_list_controller(
|
||||
page_no=paging_query.page_no,
|
||||
page_size=paging_query.page_size,
|
||||
)
|
||||
log.info("获取成功")
|
||||
|
||||
return SuccessResponse(data=result_dict, msg="获取成功")
|
||||
|
||||
@@ -56,7 +53,6 @@ async def get_online_list_controller(
|
||||
"/delete",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:online:delete"]))],
|
||||
summary="强制下线",
|
||||
description="强制下线",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def delete_online_controller(
|
||||
@@ -73,19 +69,14 @@ async def delete_online_controller(
|
||||
返回:
|
||||
- JSONResponse: 包含操作结果的JSON响应。
|
||||
"""
|
||||
is_ok = await OnlineService.delete_online_service(redis=redis, session_id=session_id)
|
||||
if is_ok:
|
||||
log.info("强制下线成功")
|
||||
return SuccessResponse(msg="强制下线成功")
|
||||
log.info("强制下线失败")
|
||||
return ErrorResponse(msg="强制下线失败")
|
||||
await OnlineService.delete_online_service(redis=redis, session_id=session_id)
|
||||
return SuccessResponse(msg="强制下线成功")
|
||||
|
||||
|
||||
@OnlineRouter.delete(
|
||||
"/clear",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:online:delete"]))],
|
||||
summary="清除所有在线用户",
|
||||
description="清除所有在线用户",
|
||||
response_model=ResponseSchema[None],
|
||||
)
|
||||
async def clear_online_controller(
|
||||
@@ -100,9 +91,5 @@ async def clear_online_controller(
|
||||
返回:
|
||||
- JSONResponse: 包含操作结果的JSON响应。
|
||||
"""
|
||||
is_ok = await OnlineService.clear_online_service(redis=redis)
|
||||
if is_ok:
|
||||
log.info("清除所有在线用户成功")
|
||||
return SuccessResponse(msg="清除所有在线用户成功")
|
||||
log.info("清除所有在线用户失败")
|
||||
return ErrorResponse(msg="清除所有在线用户失败")
|
||||
await OnlineService.clear_online_service(redis=redis)
|
||||
return SuccessResponse(msg="清除所有在线用户成功")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import Query
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.common.enums import QueueEnum
|
||||
from app.core.validator import DateTimeStr
|
||||
@@ -10,8 +10,6 @@ class OnlineOutSchema(BaseModel):
|
||||
在线用户对应pydantic模型
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
name: str = Field(..., description="用户名称")
|
||||
session_id: str = Field(..., description="会话编号")
|
||||
user_id: int = Field(..., description="用户ID")
|
||||
|
||||
@@ -3,7 +3,7 @@ import json
|
||||
from redis.asyncio.client import Redis
|
||||
|
||||
from app.common.enums import RedisInitKeyConfig
|
||||
from app.core.logger import log
|
||||
from app.core.logger import logger
|
||||
from app.core.redis_crud import RedisCURD
|
||||
from app.core.security import decode_access_token
|
||||
|
||||
@@ -41,7 +41,7 @@ class OnlineService:
|
||||
if cls._match_search_conditions(session_info, search):
|
||||
online_users.append(session_info)
|
||||
except Exception as e:
|
||||
log.error(f"解析在线用户数据失败: {e}")
|
||||
logger.error(f"解析在线用户数据失败: {e}")
|
||||
continue
|
||||
# 按照 login_time 倒序排序
|
||||
online_users.sort(key=lambda x: x.get("login_time", ""), reverse=True)
|
||||
@@ -49,41 +49,33 @@ class OnlineService:
|
||||
return online_users
|
||||
|
||||
@classmethod
|
||||
async def delete_online_service(cls, redis: Redis, session_id: str) -> bool:
|
||||
async def delete_online_service(cls, redis: Redis, session_id: str) -> None:
|
||||
"""
|
||||
强制下线指定在线用户
|
||||
|
||||
参数:
|
||||
- redis (Redis): Redis异步客户端实例。
|
||||
- session_id (str): 在线用户会话ID。
|
||||
|
||||
返回:
|
||||
- bool: 如果操作成功则返回True,否则返回False。
|
||||
"""
|
||||
# 删除 token
|
||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}")
|
||||
await RedisCURD(redis).delete(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:{session_id}")
|
||||
|
||||
log.info(f"强制下线用户会话: {session_id}")
|
||||
return True
|
||||
logger.info(f"强制下线用户会话: {session_id}")
|
||||
|
||||
@classmethod
|
||||
async def clear_online_service(cls, redis: Redis) -> bool:
|
||||
async def clear_online_service(cls, redis: Redis) -> None:
|
||||
"""
|
||||
强制下线所有在线用户
|
||||
|
||||
参数:
|
||||
- redis (Redis): Redis异步客户端实例。
|
||||
|
||||
返回:
|
||||
- bool: 如果操作成功则返回True,否则返回False。
|
||||
"""
|
||||
# 删除 token
|
||||
await RedisCURD(redis).clear(f"{RedisInitKeyConfig.ACCESS_TOKEN.key}:*")
|
||||
await RedisCURD(redis).clear(f"{RedisInitKeyConfig.REFRESH_TOKEN.key}:*")
|
||||
|
||||
log.info("清除所有在线用户会话成功")
|
||||
return True
|
||||
logger.info("清除所有在线用户会话成功")
|
||||
|
||||
@staticmethod
|
||||
def _match_search_conditions(online_info: dict, search: OnlineQueryParam | None = None) -> bool:
|
||||
|
||||
@@ -3,31 +3,32 @@ from typing import Annotated
|
||||
from fastapi import APIRouter, Body, Depends, Form, Query, Request, UploadFile
|
||||
from fastapi.responses import FileResponse, JSONResponse, StreamingResponse
|
||||
|
||||
from app.api.v1.module_common.file.service import FileService
|
||||
from app.common.request import PaginationService
|
||||
from app.common.response import ResponseSchema, StreamResponse, SuccessResponse, UploadFileResponse
|
||||
from app.core.base_params import PaginationQueryParam
|
||||
from app.core.base_schema import UploadResponseSchema
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.logger import log
|
||||
from app.core.router_class import OperationLogRoute
|
||||
from app.utils.common_util import bytes2file_response
|
||||
|
||||
from .schema import (
|
||||
ResourceCopySchema,
|
||||
ResourceCreateDirSchema,
|
||||
ResourceItemSchema,
|
||||
ResourceMoveSchema,
|
||||
ResourceRenameSchema,
|
||||
ResourceSearchQueryParam,
|
||||
)
|
||||
from .service import ResourceService
|
||||
|
||||
ResourceRouter = APIRouter(route_class=OperationLogRoute, prefix="/resource", tags=["资源管理"])
|
||||
ResourceRouter = APIRouter(route_class=OperationLogRoute, prefix="/resource", tags=["系统监控/资源管理"])
|
||||
|
||||
|
||||
@ResourceRouter.get(
|
||||
"/list",
|
||||
summary="获取目录列表",
|
||||
description="获取指定目录下的文件和子目录列表",
|
||||
response_model=ResponseSchema[list[dict]],
|
||||
response_model=ResponseSchema[list[ResourceItemSchema]],
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:query"]))],
|
||||
)
|
||||
async def get_directory_list_controller(
|
||||
@@ -57,15 +58,13 @@ async def get_directory_list_controller(
|
||||
page_size=page.page_size,
|
||||
)
|
||||
|
||||
log.info(f"获取目录列表成功: {getattr(search, 'name', None) or ''}")
|
||||
return SuccessResponse(data=result_dict, msg="获取目录列表成功")
|
||||
|
||||
|
||||
@ResourceRouter.post(
|
||||
"/upload",
|
||||
summary="上传文件",
|
||||
description="上传文件到指定目录(调用统一上传接口)",
|
||||
response_model=ResponseSchema[dict],
|
||||
response_model=ResponseSchema[UploadResponseSchema],
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:upload"]))],
|
||||
)
|
||||
async def upload_file_controller(
|
||||
@@ -85,20 +84,18 @@ async def upload_file_controller(
|
||||
- JSONResponse: 包含上传文件信息的JSON响应。
|
||||
"""
|
||||
# 调用统一上传接口,使用 resource 类型
|
||||
result_dict = await FileService.upload_service(
|
||||
result = await FileService.upload_service(
|
||||
base_url=str(request.base_url),
|
||||
file=file,
|
||||
upload_type="resource",
|
||||
target_path=target_path,
|
||||
)
|
||||
log.info(f"上传文件成功: {result_dict['file_name']}")
|
||||
return SuccessResponse(data=result_dict, msg="上传文件成功")
|
||||
return SuccessResponse(data=result, msg="上传文件成功")
|
||||
|
||||
|
||||
@ResourceRouter.get(
|
||||
"/download",
|
||||
summary="下载文件",
|
||||
description="下载指定文件",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:download"]))],
|
||||
)
|
||||
async def download_file_controller(
|
||||
@@ -120,7 +117,6 @@ async def download_file_controller(
|
||||
|
||||
filename = os.path.basename(file_path)
|
||||
|
||||
log.info(f"下载文件成功: {filename}")
|
||||
return UploadFileResponse(
|
||||
file_path=file_path,
|
||||
filename=filename,
|
||||
@@ -131,7 +127,6 @@ async def download_file_controller(
|
||||
@ResourceRouter.delete(
|
||||
"/delete",
|
||||
summary="删除文件",
|
||||
description="删除指定文件或目录",
|
||||
response_model=ResponseSchema[None],
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:delete"]))],
|
||||
)
|
||||
@@ -148,14 +143,12 @@ async def delete_files_controller(
|
||||
- JSONResponse: 包含删除结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.delete_file_service(paths=paths)
|
||||
log.info(f"删除文件成功: {paths}")
|
||||
return SuccessResponse(msg="删除文件成功")
|
||||
|
||||
|
||||
@ResourceRouter.post(
|
||||
"/move",
|
||||
summary="移动文件",
|
||||
description="移动文件或目录",
|
||||
response_model=ResponseSchema[None],
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:move"]))],
|
||||
)
|
||||
@@ -170,14 +163,12 @@ async def move_file_controller(data: ResourceMoveSchema) -> JSONResponse:
|
||||
- JSONResponse: 包含移动结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.move_file_service(data=data)
|
||||
log.info(f"移动文件成功: {data.source_path} -> {data.target_path}")
|
||||
return SuccessResponse(msg="移动文件成功")
|
||||
|
||||
|
||||
@ResourceRouter.post(
|
||||
"/copy",
|
||||
summary="复制文件",
|
||||
description="复制文件或目录",
|
||||
response_model=ResponseSchema[None],
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:copy"]))],
|
||||
)
|
||||
@@ -192,14 +183,12 @@ async def copy_file_controller(data: ResourceCopySchema) -> JSONResponse:
|
||||
- JSONResponse: 包含复制结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.copy_file_service(data=data)
|
||||
log.info(f"复制文件成功: {data.source_path} -> {data.target_path}")
|
||||
return SuccessResponse(msg="复制文件成功")
|
||||
|
||||
|
||||
@ResourceRouter.post(
|
||||
"/rename",
|
||||
summary="重命名文件",
|
||||
description="重命名文件或目录",
|
||||
response_model=ResponseSchema[None],
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:rename"]))],
|
||||
)
|
||||
@@ -214,14 +203,12 @@ async def rename_file_controller(data: ResourceRenameSchema) -> JSONResponse:
|
||||
- JSONResponse: 包含重命名结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.rename_file_service(data=data)
|
||||
log.info(f"重命名文件成功: {data.old_path} -> {data.new_name}")
|
||||
return SuccessResponse(msg="重命名文件成功")
|
||||
|
||||
|
||||
@ResourceRouter.post(
|
||||
"/create-dir",
|
||||
summary="创建目录",
|
||||
description="在指定路径创建新目录",
|
||||
response_model=ResponseSchema[None],
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:create_dir"]))],
|
||||
)
|
||||
@@ -238,14 +225,12 @@ async def create_directory_controller(
|
||||
- JSONResponse: 包含创建目录结果的JSON响应。
|
||||
"""
|
||||
await ResourceService.create_directory_service(data=data)
|
||||
log.info(f"创建目录成功: {data.parent_path}/{data.dir_name}")
|
||||
return SuccessResponse(msg="创建目录成功")
|
||||
|
||||
|
||||
@ResourceRouter.post(
|
||||
"/export",
|
||||
summary="导出资源列表",
|
||||
description="导出资源列表",
|
||||
response_model=ResponseSchema[None],
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:resource:export"]))],
|
||||
)
|
||||
@@ -268,7 +253,6 @@ async def export_resource_list_controller(
|
||||
)
|
||||
export_result = await ResourceService.export_resource_service(data_list=result_dict_list)
|
||||
|
||||
log.info("导出资源列表成功")
|
||||
return StreamResponse(
|
||||
data=bytes2file_response(export_result),
|
||||
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import ast
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import urllib.parse
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from app.config.setting import settings
|
||||
from app.core.exceptions import CustomException
|
||||
from app.core.logger import log
|
||||
from app.core.logger import logger
|
||||
from app.utils.excel_util import ExcelUtil
|
||||
|
||||
from .schema import (
|
||||
@@ -36,18 +36,18 @@ class ResourceService:
|
||||
@classmethod
|
||||
def _get_resource_root(cls) -> str:
|
||||
"""
|
||||
获取资源管理根目录(仅允许访问 upload 目录)
|
||||
获取资源管理根目录(仅允许访问 upload/resource 目录)
|
||||
|
||||
返回:
|
||||
- str: 资源管理根目录路径(upload 目录)。
|
||||
- str: 资源管理根目录路径(upload/resource 目录)。
|
||||
"""
|
||||
if not settings.STATIC_ENABLE:
|
||||
raise CustomException(msg="静态文件服务未启用")
|
||||
# 限制只能管理 upload 目录
|
||||
upload_root = os.path.join(str(settings.STATIC_ROOT), "upload")
|
||||
# 确保 upload 目录存在
|
||||
os.makedirs(upload_root, exist_ok=True)
|
||||
return upload_root
|
||||
# 限制只能管理 upload/resource 目录(与 upload_type="resource" 保持一致)
|
||||
resource_root = os.path.join(str(settings.STATIC_ROOT), "upload", "resource")
|
||||
# 确保 resource 目录存在
|
||||
os.makedirs(resource_root, exist_ok=True)
|
||||
return resource_root
|
||||
|
||||
@classmethod
|
||||
def _get_safe_path(cls, path: str | None = None) -> str:
|
||||
@@ -107,13 +107,13 @@ class ResourceService:
|
||||
|
||||
# 检查路径遍历攻击
|
||||
if ".." in path or "\x00" in path:
|
||||
log.error(f"检测到路径遍历攻击尝试: {path}")
|
||||
logger.error(f"检测到路径遍历攻击尝试: {path}")
|
||||
raise CustomException(msg="非法的路径格式")
|
||||
|
||||
# URL 解码检查
|
||||
decoded_path = urllib.parse.unquote(path)
|
||||
if ".." in decoded_path:
|
||||
log.error(f"检测到编码后的路径遍历攻击: {path}")
|
||||
logger.error(f"检测到编码后的路径遍历攻击: {path}")
|
||||
raise CustomException(msg="非法的路径格式")
|
||||
|
||||
# 构建完整路径
|
||||
@@ -128,7 +128,7 @@ class ResourceService:
|
||||
not safe_path_abs.startswith(resource_root_abs + os.sep)
|
||||
and safe_path_abs != resource_root_abs
|
||||
):
|
||||
log.error(
|
||||
logger.error(
|
||||
f"路径遍历攻击被阻止: 尝试访问 {safe_path_abs}, 但根目录是 {resource_root_abs}"
|
||||
)
|
||||
raise CustomException(msg="访问路径不在允许范围内")
|
||||
@@ -185,7 +185,7 @@ class ResourceService:
|
||||
]
|
||||
for pattern in dangerous_patterns:
|
||||
if re.search(pattern, filename, re.IGNORECASE):
|
||||
log.error(f"检测到文件名路径遍历攻击: {filename}")
|
||||
logger.error(f"检测到文件名路径遍历攻击: {filename}")
|
||||
# 返回安全文件名,不包含原始文件名
|
||||
return f"file_{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
||||
|
||||
@@ -194,7 +194,7 @@ class ResourceService:
|
||||
decoded_twice = urllib.parse.unquote(decoded)
|
||||
for check in [decoded, decoded_twice]:
|
||||
if ".." in check or "/" in check or "\\" in check:
|
||||
log.error(f"检测到编码后的文件名攻击: {filename}")
|
||||
logger.error(f"检测到编码后的文件名攻击: {filename}")
|
||||
return f"file_{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
||||
|
||||
# 使用 os.path.basename 提取纯文件名(移除路径)
|
||||
@@ -281,60 +281,48 @@ class ResourceService:
|
||||
return http_url
|
||||
|
||||
@classmethod
|
||||
def _get_file_info(cls, file_path: str, base_url: str | None = None) -> dict:
|
||||
def _get_file_info(cls, file_path: str, base_url: str | None = None) -> ResourceItemSchema | None:
|
||||
"""
|
||||
获取文件或目录的详细信息,如名称、大小、创建时间、修改时间、路径、深度、HTTP URL、是否隐藏、是否为目录等。
|
||||
获取文件或目录的详细信息。
|
||||
|
||||
参数:
|
||||
- file_path (str): 文件或目录的路径(必须是绝对路径)。
|
||||
- base_url (str | None): 基础URL,用于生成完整URL。
|
||||
|
||||
返回:
|
||||
- dict: 文件或目录的详细信息字典。
|
||||
- ResourceItemSchema | None: 文件或目录的详细信息,路径不存在时返回 None。
|
||||
"""
|
||||
try:
|
||||
# 直接使用传入的路径(已经是绝对路径)
|
||||
safe_path = file_path
|
||||
if not os.path.exists(safe_path):
|
||||
return {}
|
||||
return None
|
||||
|
||||
stat = os.stat(safe_path)
|
||||
path_obj = Path(safe_path)
|
||||
resource_root = cls._get_resource_root()
|
||||
|
||||
# 计算相对路径(相对于资源根目录)
|
||||
try:
|
||||
relative_path = os.path.relpath(safe_path, resource_root)
|
||||
except ValueError:
|
||||
relative_path = os.path.basename(safe_path)
|
||||
|
||||
# 生成HTTP URL路径
|
||||
http_url = cls._generate_http_url(safe_path, base_url)
|
||||
|
||||
# 检查是否为隐藏文件(文件名以点开头)
|
||||
is_hidden = path_obj.name.startswith(".")
|
||||
|
||||
# 对于目录,设置is_directory字段(兼容前端)
|
||||
is_directory = os.path.isdir(safe_path)
|
||||
|
||||
# 将datetime对象转换为ISO格式的字符串,确保JSON序列化成功
|
||||
created_time = datetime.fromtimestamp(stat.st_ctime).isoformat()
|
||||
modified_time = datetime.fromtimestamp(stat.st_mtime).isoformat()
|
||||
|
||||
return {
|
||||
"name": path_obj.name,
|
||||
"file_url": http_url, # 统一使用file_url字段
|
||||
"relative_path": relative_path,
|
||||
"is_file": os.path.isfile(safe_path),
|
||||
"is_dir": is_directory,
|
||||
"size": stat.st_size if os.path.isfile(safe_path) else None,
|
||||
"created_time": created_time,
|
||||
"modified_time": modified_time,
|
||||
"is_hidden": is_hidden,
|
||||
}
|
||||
return ResourceItemSchema(
|
||||
name=path_obj.name,
|
||||
file_url=http_url,
|
||||
relative_path=relative_path,
|
||||
is_file=os.path.isfile(safe_path),
|
||||
is_dir=os.path.isdir(safe_path),
|
||||
size=stat.st_size if os.path.isfile(safe_path) else None,
|
||||
created_time=datetime.fromtimestamp(stat.st_ctime),
|
||||
modified_time=datetime.fromtimestamp(stat.st_mtime),
|
||||
is_hidden=is_hidden,
|
||||
)
|
||||
except Exception as e:
|
||||
log.error(f"获取文件信息失败: {e!s}")
|
||||
return {}
|
||||
logger.error(f"获取文件信息失败: {e!s}")
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_directory_list_service(
|
||||
@@ -342,7 +330,7 @@ class ResourceService:
|
||||
path: str | None = None,
|
||||
include_hidden: bool = False,
|
||||
base_url: str | None = None,
|
||||
) -> dict:
|
||||
) -> ResourceDirectorySchema:
|
||||
"""
|
||||
获取目录列表
|
||||
|
||||
@@ -384,30 +372,30 @@ class ResourceService:
|
||||
file_info = cls._get_file_info(item_path, base_url)
|
||||
|
||||
if file_info:
|
||||
items.append(ResourceItemSchema(**file_info))
|
||||
items.append(file_info)
|
||||
|
||||
if file_info["is_file"]:
|
||||
if file_info.is_file:
|
||||
total_files += 1
|
||||
total_size += file_info.get("size", 0) or 0
|
||||
elif file_info["is_dir"]:
|
||||
total_size += file_info.size or 0
|
||||
elif file_info.is_dir:
|
||||
total_dirs += 1
|
||||
|
||||
except PermissionError:
|
||||
raise CustomException(msg="没有权限访问此目录")
|
||||
|
||||
return ResourceDirectorySchema(
|
||||
path=display_path, # 返回HTTP URL路径而不是文件系统路径
|
||||
path=display_path,
|
||||
name=os.path.basename(safe_path),
|
||||
items=items,
|
||||
total_files=total_files,
|
||||
total_dirs=total_dirs,
|
||||
total_size=total_size,
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"获取目录列表失败: {e!s}")
|
||||
logger.error(f"获取目录列表失败: {e!s}")
|
||||
raise CustomException(msg=f"获取目录列表失败: {e!s}")
|
||||
|
||||
@classmethod
|
||||
@@ -416,7 +404,7 @@ class ResourceService:
|
||||
search: ResourceSearchQueryParam | None = None,
|
||||
order_by: str | None = None,
|
||||
base_url: str | None = None,
|
||||
) -> list[dict]:
|
||||
) -> list[ResourceItemSchema]:
|
||||
"""
|
||||
搜索资源列表(用于分页和导出)
|
||||
|
||||
@@ -426,7 +414,7 @@ class ResourceService:
|
||||
- base_url (str | None): 基础URL,用于生成完整URL。
|
||||
|
||||
返回:
|
||||
- list[dict]: 资源详情字典列表。
|
||||
- list[ResourceItemSchema]: 资源详情列表。
|
||||
"""
|
||||
try:
|
||||
# 确定搜索路径
|
||||
@@ -458,7 +446,7 @@ class ResourceService:
|
||||
# 应用名称过滤
|
||||
if search and hasattr(search, "name") and search.name and search.name[1]:
|
||||
search_keyword = search.name[1].lower()
|
||||
if search_keyword not in file_info.get("name", "").lower():
|
||||
if search_keyword not in file_info.name.lower():
|
||||
continue
|
||||
|
||||
all_resources.append(file_info)
|
||||
@@ -476,16 +464,16 @@ class ResourceService:
|
||||
return sorted_resources
|
||||
|
||||
except Exception as e:
|
||||
log.error(f"搜索资源失败: {e!s}")
|
||||
logger.error(f"搜索资源失败: {e!s}")
|
||||
raise CustomException(msg=f"搜索资源失败: {e!s}")
|
||||
|
||||
@classmethod
|
||||
async def export_resource_service(cls, data_list: list[dict]) -> bytes:
|
||||
async def export_resource_service(cls, data_list: list[ResourceItemSchema]) -> bytes:
|
||||
"""
|
||||
导出资源列表
|
||||
|
||||
参数:
|
||||
- data_list (list[dict]): 资源详情字典列表。
|
||||
- data_list (list[ResourceItemSchema]): 资源详情列表。
|
||||
|
||||
返回:
|
||||
- bytes: Excel文件的二进制数据。
|
||||
@@ -500,7 +488,7 @@ class ResourceService:
|
||||
}
|
||||
|
||||
# 复制数据并转换状态
|
||||
export_data = data_list.copy()
|
||||
export_data = [item.model_dump() for item in data_list]
|
||||
|
||||
# 格式化文件大小
|
||||
for item in export_data:
|
||||
@@ -547,55 +535,38 @@ class ResourceService:
|
||||
|
||||
@classmethod
|
||||
def _sort_results(
|
||||
cls, results: list[dict], order_by: str | None = None
|
||||
) -> list[dict[Any, Any]]:
|
||||
cls, results: list[ResourceItemSchema], order_by: str | None = None
|
||||
) -> list[ResourceItemSchema]:
|
||||
"""
|
||||
排序搜索结果
|
||||
|
||||
参数:
|
||||
- results (list[dict]): 资源详情字典列表。
|
||||
- results (list[ResourceItemSchema]): 资源详情列表。
|
||||
- order_by (str | None): 排序参数。
|
||||
|
||||
返回:
|
||||
- list[dict]: 排序后的资源详情字典列表。
|
||||
- list[ResourceItemSchema]: 排序后的资源详情列表。
|
||||
"""
|
||||
try:
|
||||
# 默认按名称升序排序
|
||||
if not order_by:
|
||||
return sorted(results, key=lambda x: x.get("name", ""), reverse=False)
|
||||
return sorted(results, key=lambda x: x.name, reverse=False)
|
||||
|
||||
# 解析order_by参数,格式: [{'field':'asc/desc'}]
|
||||
|
||||
sort_conditions = eval(order_by)
|
||||
sort_conditions = ast.literal_eval(order_by)
|
||||
if isinstance(sort_conditions, list):
|
||||
# 构建排序键函数
|
||||
def sort_key(item):
|
||||
"""
|
||||
按多条排序条件从资源项中抽取比较键(支持时间字段转 datetime)。
|
||||
|
||||
参数:
|
||||
- item (dict): 单条资源详情字典。
|
||||
|
||||
返回:
|
||||
- list: 用于 `sorted` 的多字段键列表。
|
||||
"""
|
||||
keys = []
|
||||
for cond in sort_conditions:
|
||||
field = cond.get("field", "name")
|
||||
cond.get("direction", "asc")
|
||||
# 获取字段值,默认为空字符串
|
||||
value = item.get(field, "")
|
||||
# 如果是日期字段,转换为可比较的格式
|
||||
value = getattr(item, field, "")
|
||||
if (
|
||||
field
|
||||
in [
|
||||
"created_time",
|
||||
"modified_time",
|
||||
"accessed_time",
|
||||
]
|
||||
in ["created_time", "modified_time", "accessed_time"]
|
||||
and value
|
||||
):
|
||||
value = datetime.fromisoformat(value)
|
||||
if isinstance(value, str):
|
||||
value = datetime.fromisoformat(value)
|
||||
keys.append(value)
|
||||
return keys
|
||||
|
||||
@@ -634,13 +605,12 @@ class ResourceService:
|
||||
raise CustomException(msg="路径不是文件")
|
||||
|
||||
# 返回本地文件路径给 FileResponse 使用
|
||||
log.info(f"定位文件路径: {safe_path}")
|
||||
return safe_path
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"下载文件失败: {e!s}")
|
||||
logger.error(f"下载文件失败: {e!s}")
|
||||
raise CustomException(msg=f"下载文件失败: {e!s}")
|
||||
|
||||
@classmethod
|
||||
@@ -660,15 +630,13 @@ class ResourceService:
|
||||
safe_path = cls._get_safe_path(path)
|
||||
|
||||
if not os.path.exists(safe_path):
|
||||
log.error(f"路径不存在,跳过: {path}")
|
||||
logger.error(f"路径不存在,跳过: {path}")
|
||||
raise CustomException(msg=f"路径不存在: {path}")
|
||||
|
||||
if os.path.isfile(safe_path):
|
||||
os.remove(safe_path)
|
||||
log.info(f"删除文件成功: {safe_path}")
|
||||
elif os.path.isdir(safe_path):
|
||||
shutil.rmtree(safe_path)
|
||||
log.info(f"删除目录成功: {safe_path}")
|
||||
|
||||
@classmethod
|
||||
async def delete_file_service(cls, paths: list[str]) -> None:
|
||||
@@ -692,7 +660,7 @@ class ResourceService:
|
||||
try:
|
||||
cls._delete_single_path(path)
|
||||
except Exception as e:
|
||||
log.error(f"删除失败 {path}: {e!s}")
|
||||
logger.error(f"删除失败 {path}: {e!s}")
|
||||
raise CustomException(msg=f"删除失败 {path}: {e!s}")
|
||||
|
||||
@classmethod
|
||||
@@ -755,12 +723,11 @@ class ResourceService:
|
||||
|
||||
# 移动文件
|
||||
shutil.move(source_path, target_path)
|
||||
log.info(f"移动成功: {source_path} -> {target_path}")
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"移动失败: {e!s}")
|
||||
logger.error(f"移动失败: {e!s}")
|
||||
raise CustomException(msg=f"移动失败: {e!s}")
|
||||
|
||||
@classmethod
|
||||
@@ -795,12 +762,10 @@ class ResourceService:
|
||||
else:
|
||||
shutil.copytree(source_path, target_path, dirs_exist_ok=data.overwrite)
|
||||
|
||||
log.info(f"复制成功: {source_path} -> {target_path}")
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"复制失败: {e!s}")
|
||||
logger.error(f"复制失败: {e!s}")
|
||||
raise CustomException(msg=f"复制失败: {e!s}")
|
||||
|
||||
@classmethod
|
||||
@@ -826,7 +791,7 @@ class ResourceService:
|
||||
|
||||
# 如果新名称被重置,说明检测到攻击
|
||||
if safe_new_name.startswith("file_") and safe_new_name != data.new_name:
|
||||
log.error(f"重命名时检测到路径遍历攻击,原始名称: {data.new_name}")
|
||||
logger.error(f"重命名时检测到路径遍历攻击,原始名称: {data.new_name}")
|
||||
raise CustomException(msg="新名称包含非法字符")
|
||||
|
||||
# 生成新路径
|
||||
@@ -841,7 +806,7 @@ class ResourceService:
|
||||
not new_path_abs.startswith(resource_root_abs + os.sep)
|
||||
and new_path_abs != resource_root_abs
|
||||
):
|
||||
log.error(f"重命名时检测到越权访问: {new_path_abs}")
|
||||
logger.error(f"重命名时检测到越权访问: {new_path_abs}")
|
||||
raise CustomException(msg="目标路径不在允许范围内")
|
||||
|
||||
if os.path.exists(new_path):
|
||||
@@ -849,12 +814,11 @@ class ResourceService:
|
||||
|
||||
# 重命名
|
||||
os.rename(old_path, new_path)
|
||||
log.info(f"重命名成功: {old_path} -> {new_path}")
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"重命名失败: {e!s}")
|
||||
logger.error(f"重命名失败: {e!s}")
|
||||
raise CustomException(msg=f"重命名失败: {e!s}")
|
||||
|
||||
@classmethod
|
||||
@@ -882,7 +846,7 @@ class ResourceService:
|
||||
|
||||
# 如果目录名被重置,说明检测到攻击
|
||||
if safe_dir_name.startswith("file_") and safe_dir_name != data.dir_name:
|
||||
log.error(f"创建目录时检测到路径遍历攻击,原始名称: {data.dir_name}")
|
||||
logger.error(f"创建目录时检测到路径遍历攻击,原始名称: {data.dir_name}")
|
||||
raise CustomException(msg="目录名称包含非法字符")
|
||||
|
||||
# 生成新目录路径
|
||||
@@ -896,7 +860,7 @@ class ResourceService:
|
||||
not new_dir_path_abs.startswith(resource_root_abs + os.sep)
|
||||
and new_dir_path_abs != resource_root_abs
|
||||
):
|
||||
log.error(f"创建目录时检测到越权访问: {new_dir_path_abs}")
|
||||
logger.error(f"创建目录时检测到越权访问: {new_dir_path_abs}")
|
||||
raise CustomException(msg="目标路径不在允许范围内")
|
||||
|
||||
if os.path.exists(new_dir_path):
|
||||
@@ -904,12 +868,11 @@ class ResourceService:
|
||||
|
||||
# 创建目录
|
||||
os.makedirs(new_dir_path)
|
||||
log.info(f"创建目录成功: {new_dir_path}")
|
||||
|
||||
except CustomException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f"创建目录失败: {e!s}")
|
||||
logger.error(f"创建目录失败: {e!s}")
|
||||
raise CustomException(msg=f"创建目录失败: {e!s}")
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -4,18 +4,16 @@ from fastapi.responses import JSONResponse
|
||||
from app.api.v1.module_monitor.server.schema import ServerMonitorSchema
|
||||
from app.common.response import ResponseSchema, SuccessResponse
|
||||
from app.core.dependencies import AuthPermission
|
||||
from app.core.logger import log
|
||||
from app.core.router_class import OperationLogRoute
|
||||
|
||||
from .service import ServerService
|
||||
|
||||
ServerRouter = APIRouter(route_class=OperationLogRoute, prefix="/server", tags=["服务器监控"])
|
||||
ServerRouter = APIRouter(route_class=OperationLogRoute, prefix="/server", tags=["系统监控/服务器监控"])
|
||||
|
||||
|
||||
@ServerRouter.get(
|
||||
"/info",
|
||||
summary="查询服务器监控信息",
|
||||
description="查询服务器监控信息",
|
||||
dependencies=[Depends(AuthPermission(["module_monitor:server:query"]))],
|
||||
response_model=ResponseSchema[ServerMonitorSchema],
|
||||
)
|
||||
@@ -27,6 +25,5 @@ async def get_monitor_server_info_controller() -> JSONResponse:
|
||||
- JSONResponse: 包含服务器监控信息的JSON响应。
|
||||
"""
|
||||
result_dict = await ServerService.get_server_monitor_info_service()
|
||||
log.info(f"获取服务器监控信息成功: {result_dict}")
|
||||
|
||||
return SuccessResponse(data=result_dict, msg="获取服务器监控信息成功")
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class CpuInfoSchema(BaseModel):
|
||||
"""CPU信息模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
cpu_num: int = Field(description="CPU核心数")
|
||||
used: float = Field(ge=0, le=100, description="CPU用户使用率(%)")
|
||||
sys: float = Field(ge=0, le=100, description="CPU系统使用率(%)")
|
||||
@@ -15,8 +13,6 @@ class CpuInfoSchema(BaseModel):
|
||||
class MemoryInfoSchema(BaseModel):
|
||||
"""内存信息模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
total: str = Field(description="内存总量")
|
||||
used: str = Field(description="已用内存")
|
||||
free: str = Field(description="剩余内存")
|
||||
@@ -26,8 +22,6 @@ class MemoryInfoSchema(BaseModel):
|
||||
class SysInfoSchema(BaseModel):
|
||||
"""系统信息模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
computer_ip: str = Field(description="服务器IP")
|
||||
computer_name: str = Field(description="服务器名称")
|
||||
os_arch: str = Field(description="系统架构")
|
||||
@@ -38,8 +32,6 @@ class SysInfoSchema(BaseModel):
|
||||
class PyInfoSchema(BaseModel):
|
||||
"""Python运行信息模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
name: str = Field(description="Python名称")
|
||||
version: str = Field(description="Python版本")
|
||||
start_time: str = Field(description="启动时间")
|
||||
@@ -54,8 +46,6 @@ class PyInfoSchema(BaseModel):
|
||||
class DiskInfoSchema(BaseModel):
|
||||
"""磁盘信息模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
dir_name: str = Field(description="磁盘路径")
|
||||
sys_type_name: str = Field(description="文件系统类型")
|
||||
type_name: str = Field(description="磁盘类型")
|
||||
@@ -68,8 +58,6 @@ class DiskInfoSchema(BaseModel):
|
||||
class ServerMonitorSchema(BaseModel):
|
||||
"""服务器监控信息模型"""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
cpu: CpuInfoSchema = Field(description="CPU信息")
|
||||
mem: MemoryInfoSchema = Field(description="内存信息")
|
||||
py: PyInfoSchema = Field(description="Python运行信息")
|
||||
|
||||
@@ -21,7 +21,7 @@ class ServerService:
|
||||
"""服务监控模块服务层"""
|
||||
|
||||
@classmethod
|
||||
async def get_server_monitor_info_service(cls) -> dict:
|
||||
async def get_server_monitor_info_service(cls) -> ServerMonitorSchema:
|
||||
"""
|
||||
获取服务器监控信息
|
||||
|
||||
@@ -34,7 +34,7 @@ class ServerService:
|
||||
sys=cls._get_system_info(),
|
||||
py=cls._get_python_info(),
|
||||
disks=cls._get_disk_info(),
|
||||
).model_dump()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_cpu_info(cls) -> CpuInfoSchema:
|
||||
|
||||
Reference in New Issue
Block a user