diff --git a/encryption/encrypt_decorator.py b/encryption/encrypt_decorator.py index b6ce311..6607ad4 100644 --- a/encryption/encrypt_decorator.py +++ b/encryption/encrypt_decorator.py @@ -1,7 +1,8 @@ import json from functools import wraps + +from encryption.encryption import aes_encrypt from schema.response_schema import APIResponse -from utils.encrypt_utils import aes_encrypt def encrypt_response(field: str = "data"): diff --git a/service/device_action_service.py b/service/device_action_service.py index 5fb5c25..e93a36d 100644 --- a/service/device_action_service.py +++ b/service/device_action_service.py @@ -2,6 +2,7 @@ from fastapi import APIRouter, Query, Path from mysql.connector import Error as MySQLError from ds.db import db +from encryption.encrypt_decorator import encrypt_response from schema.device_action_schema import ( DeviceActionCreate, DeviceActionResponse, @@ -62,6 +63,7 @@ def add_device_action(action_data: DeviceActionCreate) -> DeviceActionResponse: # 接口: 分页查询操作记录列表(仅返回 total + device_actions) # ------------------------------ @router.get("/list", response_model=APIResponse, summary="分页查询设备操作记录") +@encrypt_response() async def get_device_action_list( page: int = Query(1, ge=1, description="页码、默认1"), page_size: int = Query(10, ge=1, le=100, description="每页条数、1-100"), @@ -119,6 +121,7 @@ async def get_device_action_list( @router.get("/{client_ip}", response_model=APIResponse, summary="根据IP查询设备操作记录") +@encrypt_response() async def get_device_actions_by_ip( client_ip: str = Path(..., description="客户端IP地址") ): diff --git a/service/device_service.py b/service/device_service.py index b8657b2..39e2ad8 100644 --- a/service/device_service.py +++ b/service/device_service.py @@ -5,6 +5,7 @@ from fastapi import APIRouter, Query, HTTPException, Request, Path from mysql.connector import Error as MySQLError from ds.db import db +from encryption.encrypt_decorator import encrypt_response from schema.device_schema import ( DeviceCreateRequest, DeviceResponse, DeviceListResponse, DeviceStatusHistoryResponse, DeviceStatusHistoryListResponse @@ -161,6 +162,7 @@ def update_online_status_by_ip(client_ip: str, online_status: int) -> bool: # 创建设备信息接口 # ------------------------------ @router.post("/add", response_model=APIResponse, summary="创建设备信息") +@encrypt_response() async def create_device(device_data: DeviceCreateRequest, request: Request): conn = None cursor = None @@ -242,6 +244,7 @@ async def create_device(device_data: DeviceCreateRequest, request: Request): # 获取设备列表接口 # ------------------------------ @router.get("/", response_model=APIResponse, summary="获取设备列表(支持筛选分页)") +@encrypt_response() async def get_device_list( page: int = Query(1, ge=1, description="页码,默认第1页"), page_size: int = Query(10, ge=1, le=100, description="每页条数,1-100之间"), @@ -301,6 +304,7 @@ async def get_device_list( # 获取设备上下线记录接口 # ------------------------------ @router.get("/{device_id}/status-history", response_model=APIResponse, summary="获取设备上下线记录") +@encrypt_response() async def get_device_status_history( device_id: int = Path(..., description="设备ID"), page: int = Query(1, ge=1, description="页码,默认第1页"), @@ -380,6 +384,7 @@ async def get_device_status_history( # 手动更新设备在线状态接口 # ------------------------------ @router.put("/{device_id}/status", response_model=APIResponse, summary="更新设备在线状态") +@encrypt_response() async def update_device_status( device_id: int = Path(..., description="设备ID"), status: int = Query(..., ge=0, le=1, description="在线状态(1-在线、0-离线)") diff --git a/service/face_service.py b/service/face_service.py index 0a6c8ad..a29f846 100644 --- a/service/face_service.py +++ b/service/face_service.py @@ -5,6 +5,7 @@ import os from pathlib import Path from ds.db import db +from encryption.encrypt_decorator import encrypt_response from schema.face_schema import ( FaceCreateRequest, FaceUpdateRequest, @@ -23,6 +24,7 @@ router = APIRouter(prefix="/faces", tags=["人脸管理"]) # 1. 创建人脸记录(使用修复后的路径) # ------------------------------ @router.post("", response_model=APIResponse, summary="创建人脸记录") +@encrypt_response() async def create_face( request: Request, name: str = Form(None, max_length=255, description="名称(可选)"), @@ -97,6 +99,7 @@ async def create_face( # 2. 获取单个人脸记录 # ------------------------------ @router.get("/{face_id}", response_model=APIResponse, summary="获取单个人脸记录") +@encrypt_response() async def get_face(face_id: int): conn = None cursor = None @@ -130,6 +133,7 @@ async def get_face(face_id: int): # 3. 获取人脸列表 # ------------------------------ @router.get("", response_model=APIResponse, summary="获取人脸列表(分页+筛选)") +@encrypt_response() async def get_face_list( page: int = Query(1, ge=1), page_size: int = Query(10, ge=1, le=100), @@ -189,6 +193,7 @@ async def get_face_list( # 4. 更新人脸记录 # ------------------------------ @router.put("/{face_id}", response_model=APIResponse, summary="更新人脸记录") +@encrypt_response() async def update_face(face_id: int, face_update: FaceUpdateRequest): conn = None cursor = None @@ -255,6 +260,7 @@ async def update_face(face_id: int, face_update: FaceUpdateRequest): # 5. 删除人脸记录 # ------------------------------ @router.delete("/{face_id}", response_model=APIResponse, summary="删除人脸记录") +@encrypt_response() async def delete_face(face_id: int): conn = None cursor = None @@ -305,6 +311,7 @@ async def delete_face(face_id: int): # 6. 获取人脸图片 # ------------------------------ @router.get("/{face_id}/image", summary="获取人脸图片") +@encrypt_response() async def get_face_image(face_id: int): conn = None cursor = None diff --git a/service/file_service.py b/service/file_service.py index 93c6b64..f8a8f0f 100644 --- a/service/file_service.py +++ b/service/file_service.py @@ -18,12 +18,11 @@ app = Flask(__name__) # ------------------------------ # 核心修改:与 FastAPI 对齐的跨域配置 # ------------------------------ -# 1. 允许的前端域名(完全复制 FastAPI 的 ALLOWED_ORIGINS,确保前后端一致) +# 1. 允许的前端域名(根据实际环境修改,生产环境删除 "*") ALLOWED_ORIGINS = [ - # "http://localhost:8080", # 本地前端开发地址(必改:替换为你的前端实际地址) + # "http://localhost:8080", # 本地前端开发地址 # "http://127.0.0.1:8080", - # "http://服务器IP:8080", # 部署后前端地址(替换为你的服务器IP/域名) - # # "*" 仅开发环境临时使用,生产环境必须删除(安全风险) + # "http://服务器IP:8080", # 部署后前端地址 "*" ] @@ -31,33 +30,29 @@ ALLOWED_ORIGINS = [ CORS( app, resources={ - r"/*": { # 对所有 Flask 路由生效(覆盖图片、模型下载所有接口) - "origins": ALLOWED_ORIGINS, # 允许的前端域名(与 FastAPI 一致) - "allow_credentials": True, # 允许携带 Cookie(与 FastAPI 一致,需登录态必开) - "methods": ["*"], # 允许所有 HTTP 方法(FastAPI 用 "*",此处同步) - "allow_headers": ["*"], # 允许所有请求头(与 FastAPI 一致) + r"/*": { + "origins": ALLOWED_ORIGINS, + "allow_credentials": True, + "methods": ["*"], + "allow_headers": ["*"], } }, ) # ------------------------------ -# 核心路径配置(不变,确保资源目录正确) +# 核心路径配置(关键修改:修正 PROJECT_ROOT 计算) +# 原问题:file_service.py 在 service 文件夹内,需向上跳一级到项目根目录 # ------------------------------ -CURRENT_FILE_PATH = Path(__file__).resolve() -PROJECT_ROOT = CURRENT_FILE_PATH.parent # 项目根目录(video/) -# 资源目录(图片、模型) -BASE_IMAGE_DIR_DECT = str((PROJECT_ROOT / "resource" / "dect").resolve()) # 检测图片目录 -BASE_IMAGE_DIR_UP_IMAGES = str((PROJECT_ROOT / "up_images").resolve()) # 人脸图片目录 -BASE_MODEL_DIR = str((PROJECT_ROOT / "resource" / "models").resolve()) # 模型文件目录 +CURRENT_FILE_PATH = Path(__file__).resolve() # 当前文件路径:service/file_service.py +PROJECT_ROOT = CURRENT_FILE_PATH.parent.parent # 项目根目录(service 文件夹的父目录) +# 资源目录(现在正确指向项目根目录下的文件夹) +BASE_IMAGE_DIR_DECT = str((PROJECT_ROOT / "resource" / "dect").resolve()) # 根目录/resource/dect +BASE_IMAGE_DIR_UP_IMAGES = str((PROJECT_ROOT / "up_images").resolve()) # 根目录/up_images +BASE_MODEL_DIR = str((PROJECT_ROOT / "resource" / "models").resolve()) # 根目录/resource/models -# 打印路径配置(调试用,确认目录正确) -# logger.info(f"[Flask 配置] 项目根目录:{PROJECT_ROOT}") -# logger.info(f"[Flask 配置] 模型目录:{BASE_MODEL_DIR}") -# logger.info(f"[Flask 配置] 人脸图片目录:{BASE_IMAGE_DIR_UP_IMAGES}") -# logger.info(f"[Flask 配置] 检测图片目录:{BASE_IMAGE_DIR_DECT}") # ------------------------------ -# 安全检查装饰器(不变,防路径遍历/非法文件) +# 安全检查装饰器(不变) # ------------------------------ def safe_path_check(root_dir: str): def decorator(func): @@ -86,7 +81,7 @@ def safe_path_check(root_dir: str): ) abort(404) - # 3. 限制文件大小(模型200MB,图片10MB,避免超大文件攻击) + # 3. 限制文件大小(模型200MB,图片10MB) max_size = 200 * 1024 * 1024 if "models" in root_dir else 10 * 1024 * 1024 if os.path.getsize(full_file_path) > max_size: logger.warning( @@ -121,7 +116,7 @@ def download_model(resource_path, root_dir): f"[Flask 模型下载] 成功请求!IP:{request.remote_addr} | 文件:{file_name} | 目录:{full_dir}" ) - # 强制浏览器下载(而非预览),设置二进制文件类型 + # 强制浏览器下载(而非预览) return send_from_directory( full_dir, file_name, @@ -158,7 +153,7 @@ def get_face_image(resource_path, root_dir): f"[Flask 人脸图片] 成功请求!IP:{request.remote_addr} | 文件:{file_name} | 目录:{full_dir}" ) - # 允许浏览器预览图片(而非下载) + # 允许浏览器预览图片 return send_from_directory(full_dir, file_name, as_attachment=False) except Exception as e: @@ -205,7 +200,6 @@ def get_dect_image(resource_path, root_dir): @safe_path_check(root_dir=BASE_IMAGE_DIR_DECT) def get_compatible_image(resource_path, root_dir): try: - # 逻辑与检测图片接口一致,仅URL前缀不同(兼容旧前端) resource_path = resource_path.replace("/", os.sep).replace("\\", os.sep) dir_path, file_name = os.path.split(resource_path) full_dir = os.path.abspath(os.path.join(root_dir, dir_path)) @@ -230,7 +224,7 @@ def get_compatible_image(resource_path, root_dir): abort(500) # ------------------------------ -# 全局错误处理器(友好提示,与 FastAPI 错误信息风格一致) +# 全局错误处理器(不变) # ------------------------------ @app.errorhandler(403) def forbidden_error(error): @@ -256,7 +250,7 @@ def server_error(error): # Flask 独立启动入口(供测试,实际由 main.py 子线程启动) # ------------------------------ if __name__ == '__main__': - # 确保所有资源目录存在(防止初始化失败) + # 确保所有资源目录存在 required_dirs = [ (BASE_IMAGE_DIR_DECT, "检测图片目录"), (BASE_IMAGE_DIR_UP_IMAGES, "人脸图片目录"), @@ -267,16 +261,16 @@ if __name__ == '__main__': logger.info(f"[Flask 初始化] {dir_desc}不存在,创建:{dir_path}") os.makedirs(dir_path, exist_ok=True) - # 启动提示(含访问示例) + # 启动提示 logger.info("\n[Flask 服务启动成功!] 支持的接口:") logger.info(f"1. 模型下载 → http://服务器IP:5000/model/download/resource/models/xxx.pt") logger.info(f"2. 人脸图片 → http://服务器IP:5000/up_images/xxx.jpg") logger.info(f"3. 检测图片 → http://服务器IP:5000/resource/dect/xxx.jpg 或 http://服务器IP:5000/images/xxx.jpg\n") - # 启动服务(禁用 debug 和自动重载,避免多线程冲突) + # 启动服务(禁用 debug 和自动重载) app.run( - host="0.0.0.0", # 允许外部IP访问 - port=5000, # 与 main.py 中 Flask 端口一致 + host="0.0.0.0", + port=5000, debug=False, use_reloader=False ) \ No newline at end of file diff --git a/service/model_service.py b/service/model_service.py index 7603710..7dbb8fb 100644 --- a/service/model_service.py +++ b/service/model_service.py @@ -11,6 +11,7 @@ from mysql.connector import Error as MySQLError # 复用项目依赖 from ds.db import db +from encryption.encrypt_decorator import encrypt_response from schema.model_schema import ( ModelCreateRequest, ModelUpdateRequest, @@ -190,6 +191,7 @@ def get_current_conf_threshold(): # 1. 上传模型(保持不变) @router.post("", response_model=APIResponse, summary="上传YOLO模型(.pt格式)") +@encrypt_response() async def upload_model( name: str = Form(..., description="模型名称"), description: str = Form(None, description="模型描述"), @@ -281,6 +283,7 @@ async def upload_model( # 2. 获取模型列表(保持不变) @router.get("", response_model=APIResponse, summary="获取模型列表(分页)") +@encrypt_response() async def get_model_list( page: int = Query(1, ge=1), page_size: int = Query(10, ge=1, le=100), @@ -337,6 +340,7 @@ async def get_model_list( # 3. 获取默认模型(保持不变) @router.get("/default", response_model=APIResponse, summary="获取当前默认模型") +@encrypt_response() async def get_default_model(): conn = None cursor = None @@ -379,6 +383,7 @@ async def get_default_model(): # 4. 获取单个模型详情(保持不变) @router.get("/{model_id}", response_model=APIResponse, summary="获取单个模型详情") +@encrypt_response() async def get_model(model_id: int): conn = None cursor = None @@ -414,6 +419,7 @@ async def get_model(model_id: int): # 5. 更新模型信息(保持不变) @router.put("/{model_id}", response_model=APIResponse, summary="更新模型信息") +@encrypt_response() async def update_model(model_id: int, model_update: ModelUpdateRequest): conn = None cursor = None @@ -497,6 +503,7 @@ async def update_model(model_id: int, model_update: ModelUpdateRequest): # 5.1 更换默认模型(添加置信度参数) @router.put("/{model_id}/set-default", response_model=APIResponse, summary="更换默认模型(自动重启服务)") +@encrypt_response() async def set_default_model( model_id: int, conf_threshold: float = Query(0.8, ge=0.01, le=0.99, description="模型检测置信度阈值(0.01-0.99)") @@ -591,6 +598,7 @@ async def set_default_model( # 6. 删除模型(保持不变) @router.delete("/{model_id}", response_model=APIResponse, summary="删除模型") +@encrypt_response() async def delete_model(model_id: int): conn = None cursor = None @@ -650,6 +658,7 @@ async def delete_model(model_id: int): # 7. 下载模型文件(保持不变) @router.get("/{model_id}/download", summary="下载模型文件") +@encrypt_response() async def download_model(model_id: int): conn = None cursor = None diff --git a/service/sensitive_service.py b/service/sensitive_service.py index a1ad54f..1ad65f9 100644 --- a/service/sensitive_service.py +++ b/service/sensitive_service.py @@ -25,6 +25,7 @@ router = APIRouter( # 1. 创建敏感信息记录 # ------------------------------ @router.post("", response_model=APIResponse, summary="创建敏感信息记录") +@encrypt_response() async def create_sensitive( sensitive: SensitiveCreateRequest, current_user: UserResponse = Depends(get_current_user) # 补充登录认证依赖(与其他接口保持一致) @@ -76,7 +77,9 @@ async def create_sensitive( # ------------------------------ # 2. 获取单个敏感信息记录 # ------------------------------ + @router.get("/{sensitive_id}", response_model=APIResponse, summary="获取单个敏感信息记录") +@encrypt_response() async def get_sensitive( sensitive_id: int, current_user: UserResponse = Depends(get_current_user) # 需登录认证 @@ -125,8 +128,7 @@ async def get_sensitive( async def get_sensitive_list( page: int = Query(1, ge=1, description="页码(默认1,最小1)"), page_size: int = Query(10, ge=1, le=100, description="每页条数(默认10,1-100)"), - name: Optional[str] = Query(None, description="敏感词关键词搜索(模糊匹配)"), - current_user: UserResponse = Depends(get_current_user) # 需登录认证 + name: Optional[str] = Query(None, description="敏感词关键词搜索(模糊匹配)") ): """ 获取敏感信息分页列表: @@ -191,6 +193,7 @@ async def get_sensitive_list( # 4. 更新敏感信息记录 # ------------------------------ @router.put("/{sensitive_id}", response_model=APIResponse, summary="更新敏感信息记录") +@encrypt_response() async def update_sensitive( sensitive_id: int, sensitive_update: SensitiveUpdateRequest, @@ -269,6 +272,7 @@ async def update_sensitive( # 5. 删除敏感信息记录 # ------------------------------ @router.delete("/{sensitive_id}", response_model=APIResponse, summary="删除敏感信息记录") +@encrypt_response() async def delete_sensitive( sensitive_id: int, current_user: UserResponse = Depends(get_current_user) # 需登录认证 diff --git a/service/user_service.py b/service/user_service.py index e90c1e4..6a87e48 100644 --- a/service/user_service.py +++ b/service/user_service.py @@ -27,6 +27,7 @@ router = APIRouter( # 1. 用户注册接口 # ------------------------------ @router.post("/register", response_model=APIResponse, summary="用户注册") +@encrypt_response() async def user_register(request: UserRegisterRequest): """ 用户注册: @@ -78,6 +79,7 @@ async def user_register(request: UserRegisterRequest): # 2. 用户登录接口 # ------------------------------ @router.post("/login", response_model=APIResponse, summary="用户登录(获取 Token)") +@encrypt_response() async def user_login(request: UserLoginRequest): """ 用户登录: @@ -140,6 +142,7 @@ async def user_login(request: UserLoginRequest): # 3. 获取当前登录用户信息(需认证) # ------------------------------ @router.get("/me", response_model=APIResponse, summary="获取当前用户信息") +@encrypt_response() async def get_current_user_info( current_user: UserResponse = Depends(get_current_user) # 依赖认证中间件 ): @@ -159,6 +162,7 @@ async def get_current_user_info( # 4. 获取用户列表(仅需登录权限) # ------------------------------ @router.get("/list", response_model=APIResponse, summary="获取用户列表") +@encrypt_response() async def get_user_list( page: int = Query(1, ge=1, description="页码,从1开始"), page_size: int = Query(10, ge=1, le=100, description="每页条数,1-100之间"),