diff --git a/src/meshcore_hub/api/auth.py b/src/meshcore_hub/api/auth.py index dddb38d..73b874c 100644 --- a/src/meshcore_hub/api/auth.py +++ b/src/meshcore_hub/api/auth.py @@ -83,8 +83,9 @@ async def require_read( return token raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, + status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key", + headers={"WWW-Authenticate": "Bearer"}, ) diff --git a/src/meshcore_hub/api/routes/__init__.py b/src/meshcore_hub/api/routes/__init__.py index c9a5934..e152edc 100644 --- a/src/meshcore_hub/api/routes/__init__.py +++ b/src/meshcore_hub/api/routes/__init__.py @@ -25,4 +25,4 @@ api_router.include_router( ) api_router.include_router(telemetry_router, prefix="/telemetry", tags=["Telemetry"]) api_router.include_router(commands_router, prefix="/commands", tags=["Commands"]) -api_router.include_router(dashboard_router, tags=["Dashboard"]) +api_router.include_router(dashboard_router, prefix="/dashboard", tags=["Dashboard"]) diff --git a/src/meshcore_hub/api/routes/messages.py b/src/meshcore_hub/api/routes/messages.py index e2f255a..4c57731 100644 --- a/src/meshcore_hub/api/routes/messages.py +++ b/src/meshcore_hub/api/routes/messages.py @@ -18,7 +18,7 @@ router = APIRouter() async def list_messages( _: RequireRead, session: DbSession, - type: Optional[str] = Query(None, description="Filter by message type"), + message_type: Optional[str] = Query(None, description="Filter by message type"), pubkey_prefix: Optional[str] = Query(None, description="Filter by sender prefix"), channel_idx: Optional[int] = Query(None, description="Filter by channel"), since: Optional[datetime] = Query(None, description="Start timestamp"), @@ -31,8 +31,8 @@ async def list_messages( # Build query query = select(Message) - if type: - query = query.where(Message.message_type == type) + if message_type: + query = query.where(Message.message_type == message_type) if pubkey_prefix: query = query.where(Message.pubkey_prefix == pubkey_prefix) diff --git a/src/meshcore_hub/api/routes/node_tags.py b/src/meshcore_hub/api/routes/node_tags.py index 2da9c2b..116530a 100644 --- a/src/meshcore_hub/api/routes/node_tags.py +++ b/src/meshcore_hub/api/routes/node_tags.py @@ -28,6 +28,33 @@ async def list_node_tags( return [NodeTagRead.model_validate(t) for t in node.tags] +@router.get("/nodes/{public_key}/tags/{key}", response_model=NodeTagRead) +async def get_node_tag( + _: RequireRead, + session: DbSession, + public_key: str, + key: str, +) -> NodeTagRead: + """Get a specific tag for a node.""" + # Find node + node_query = select(Node).where(Node.public_key == public_key) + node = session.execute(node_query).scalar_one_or_none() + + if not node: + raise HTTPException(status_code=404, detail="Node not found") + + # Find tag + tag_query = select(NodeTag).where( + (NodeTag.node_id == node.id) & (NodeTag.key == key) + ) + node_tag = session.execute(tag_query).scalar_one_or_none() + + if not node_tag: + raise HTTPException(status_code=404, detail="Tag not found") + + return NodeTagRead.model_validate(node_tag) + + @router.post("/nodes/{public_key}/tags", response_model=NodeTagRead, status_code=201) async def create_node_tag( _: RequireAdmin,