增加API鉴权

This commit is contained in:
jeremygan2021
2025-11-16 18:00:28 +08:00
parent bb04bd8fa5
commit b7a8a86e53
23 changed files with 343 additions and 52 deletions

View File

@@ -3,7 +3,7 @@ from api import devices, contents, todos
api_router = APIRouter()
# 注册所有路由
api_router.include_router(devices.router)
api_router.include_router(contents.router)
api_router.include_router(todos.router)
# 注册所有路由,并添加全局安全要求
api_router.include_router(devices.router, prefix="/devices")
api_router.include_router(contents.router, prefix="/contents")
api_router.include_router(todos.router, prefix="/todos")

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, status, Query, File, UploadFile
from fastapi import APIRouter, Depends, HTTPException, status, Query, File, UploadFile, Security
from sqlalchemy.orm import Session
from sqlalchemy import func
from typing import List, Optional
@@ -11,13 +11,13 @@ from models import Content as ContentModel, Device as DeviceModel
from mqtt_manager import mqtt_manager
from image_processor import image_processor
from config import settings
from auth import get_api_key
router = APIRouter(
prefix="/api",
tags=["contents"]
)
@router.post("/devices/{device_id}/content", response_model=ContentSchema, status_code=status.HTTP_201_CREATED)
@router.post("/devices/{device_id}/content", response_model=ContentSchema, status_code=status.HTTP_201_CREATED, dependencies=[Depends(get_api_key)])
async def create_content(
device_id: str,
content: ContentCreate,
@@ -60,7 +60,7 @@ async def create_content(
return db_content
@router.get("/devices/{device_id}/content", response_model=List[ContentSchema])
@router.get("/devices/{device_id}/content", response_model=List[ContentSchema], dependencies=[Depends(get_api_key)])
async def list_content(
device_id: str,
skip: int = 0,
@@ -87,7 +87,7 @@ async def list_content(
contents = query.order_by(ContentModel.version.desc()).offset(skip).limit(limit).all()
return contents
@router.get("/devices/{device_id}/content/{version}", response_model=ContentResponse)
@router.get("/devices/{device_id}/content/{version}", response_model=ContentResponse, dependencies=[Depends(get_api_key)])
async def get_content(
device_id: str,
version: int,
@@ -141,7 +141,7 @@ async def get_content(
created_at=content.created_at
)
@router.put("/devices/{device_id}/content/{version}", response_model=ContentSchema)
@router.put("/devices/{device_id}/content/{version}", response_model=ContentSchema, dependencies=[Depends(get_api_key)])
async def update_content(
device_id: str,
version: int,
@@ -176,7 +176,7 @@ async def update_content(
return content
@router.delete("/devices/{device_id}/content/{version}", status_code=status.HTTP_204_NO_CONTENT)
@router.delete("/devices/{device_id}/content/{version}", status_code=status.HTTP_204_NO_CONTENT, dependencies=[Depends(get_api_key)])
async def delete_content(
device_id: str,
version: int,

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, status, Security
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import datetime
@@ -9,13 +9,13 @@ from schemas import Device as DeviceSchema, DeviceCreate, DeviceUpdate, Bootstra
from models import Device as DeviceModel
from database import Content as ContentModel
from mqtt_manager import mqtt_manager
from auth import get_api_key
router = APIRouter(
prefix="/api/devices",
tags=["devices"]
)
@router.post("/", response_model=DeviceSchema, status_code=status.HTTP_201_CREATED)
@router.post("/", response_model=DeviceSchema, status_code=status.HTTP_201_CREATED, dependencies=[Depends(get_api_key)])
async def create_device(device: DeviceCreate, db: Session = Depends(get_db)):
"""
注册新设备
@@ -46,7 +46,7 @@ async def create_device(device: DeviceCreate, db: Session = Depends(get_db)):
return db_device
@router.get("/", response_model=List[DeviceSchema])
@router.get("/", response_model=List[DeviceSchema], dependencies=[Depends(get_api_key)])
async def list_devices(
skip: int = 0,
limit: int = 100,
@@ -68,7 +68,7 @@ async def list_devices(
devices = query.offset(skip).limit(limit).all()
return devices
@router.get("/{device_id}", response_model=DeviceSchema)
@router.get("/{device_id}", response_model=DeviceSchema, dependencies=[Depends(get_api_key)])
async def get_device(device_id: str, db: Session = Depends(get_db)):
"""
获取设备详情
@@ -81,7 +81,7 @@ async def get_device(device_id: str, db: Session = Depends(get_db)):
)
return device
@router.put("/{device_id}", response_model=DeviceSchema)
@router.put("/{device_id}", response_model=DeviceSchema, dependencies=[Depends(get_api_key)])
async def update_device(
device_id: str,
device_update: DeviceUpdate,
@@ -108,7 +108,7 @@ async def update_device(
return device
@router.delete("/{device_id}", status_code=status.HTTP_204_NO_CONTENT)
@router.delete("/{device_id}", status_code=status.HTTP_204_NO_CONTENT, dependencies=[Depends(get_api_key)])
async def delete_device(device_id: str, db: Session = Depends(get_db)):
"""
删除设备
@@ -126,11 +126,12 @@ async def delete_device(device_id: str, db: Session = Depends(get_db)):
db.delete(device)
db.commit()
@router.get("/{device_id}/bootstrap", response_model=BootstrapResponse)
async def device_bootstrap(device_id: str, db: Session = Depends(get_db)):
@router.post("/{device_id}/bootstrap", response_model=BootstrapResponse, dependencies=[Depends(get_api_key)])
async def bootstrap_device(device_id: str, db: Session = Depends(get_db)):
"""
设备启动获取当前版本信息
设备引导 - 获取设备配置和内容
"""
# 验证设备是否存在
device = db.query(DeviceModel).filter(DeviceModel.device_id == device_id).first()
if not device:
raise HTTPException(
@@ -138,29 +139,19 @@ async def device_bootstrap(device_id: str, db: Session = Depends(get_db)):
detail="设备不存在"
)
# 更新设备最后在线时间
device.last_online = datetime.utcnow()
db.commit()
# 获取最新的活跃内容
latest_content = db.query(ContentModel).filter(
ContentModel.device_id == device_id,
ContentModel.is_active == True
).order_by(ContentModel.version.desc()).first()
# 获取设备场景的内容
contents = db.query(ContentModel).filter(ContentModel.scene == device.scene).all()
# 构建响应
response = BootstrapResponse(
device_id=device_id,
timezone=latest_content.timezone if latest_content else "Asia/Shanghai",
time_format=latest_content.time_format if latest_content else "%Y-%m-%d %H:%M"
scene=device.scene,
contents=contents
)
if latest_content:
response.content_version = latest_content.version
response.last_updated = latest_content.created_at
return response
@router.get("/{device_id}/status")
@router.get("/{device_id}/status", dependencies=[Depends(get_api_key)])
async def get_device_status(device_id: str, db: Session = Depends(get_db)):
"""
获取设备状态

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, status, Security
from sqlalchemy.orm import Session
from typing import List, Optional
from datetime import datetime
@@ -7,13 +7,13 @@ from database import get_db
from schemas import Todo as TodoSchema, TodoCreate, TodoUpdate
from models import Todo as TodoModel
from database import Device as DeviceModel
from auth import get_api_key
router = APIRouter(
prefix="/api/todos",
tags=["todos"]
)
@router.post("/", response_model=TodoSchema, status_code=status.HTTP_201_CREATED)
@router.post("/", response_model=TodoSchema, status_code=status.HTTP_201_CREATED, dependencies=[Depends(get_api_key)])
async def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
"""
创建新的待办事项
@@ -40,7 +40,7 @@ async def create_todo(todo: TodoCreate, db: Session = Depends(get_db)):
return db_todo
@router.get("/", response_model=List[TodoSchema])
@router.get("/", response_model=List[TodoSchema], dependencies=[Depends(get_api_key)])
async def list_todos(
skip: int = 0,
limit: int = 100,
@@ -62,7 +62,7 @@ async def list_todos(
todos = query.order_by(TodoModel.created_at.desc()).offset(skip).limit(limit).all()
return todos
@router.get("/{todo_id}", response_model=TodoSchema)
@router.get("/{todo_id}", response_model=TodoSchema, dependencies=[Depends(get_api_key)])
async def get_todo(todo_id: int, db: Session = Depends(get_db)):
"""
获取待办事项详情
@@ -75,7 +75,7 @@ async def get_todo(todo_id: int, db: Session = Depends(get_db)):
)
return todo
@router.put("/{todo_id}", response_model=TodoSchema)
@router.put("/{todo_id}", response_model=TodoSchema, dependencies=[Depends(get_api_key)])
async def update_todo(
todo_id: int,
todo_update: TodoUpdate,
@@ -110,7 +110,7 @@ async def update_todo(
return todo
@router.delete("/{todo_id}", status_code=status.HTTP_204_NO_CONTENT)
@router.delete("/{todo_id}", status_code=status.HTTP_204_NO_CONTENT, dependencies=[Depends(get_api_key)])
async def delete_todo(todo_id: int, db: Session = Depends(get_db)):
"""
删除待办事项
@@ -125,7 +125,7 @@ async def delete_todo(todo_id: int, db: Session = Depends(get_db)):
db.delete(todo)
db.commit()
@router.post("/{todo_id}/complete", response_model=TodoSchema)
@router.post("/{todo_id}/complete", response_model=TodoSchema, dependencies=[Depends(get_api_key)])
async def complete_todo(todo_id: int, db: Session = Depends(get_db)):
"""
标记待办事项为完成
@@ -146,7 +146,7 @@ async def complete_todo(todo_id: int, db: Session = Depends(get_db)):
return todo
@router.post("/{todo_id}/incomplete", response_model=TodoSchema)
@router.post("/{todo_id}/incomplete", response_model=TodoSchema, dependencies=[Depends(get_api_key)])
async def incomplete_todo(todo_id: int, db: Session = Depends(get_db)):
"""
标记待办事项为未完成