| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- """
- Device API endpoint for reporting tunnel port allocation.
- """
- from typing import Annotated, Optional
- from fastapi import APIRouter, Depends, Header, HTTPException, status
- from pydantic import BaseModel
- from sqlalchemy import select
- from sqlalchemy.ext.asyncio import AsyncSession
- from app.core.database import get_db
- from app.models.device import Device
- from app.services.tunnel_service import tunnel_service
- router = APIRouter(tags=["device-api"])
- class TunnelPortRequest(BaseModel):
- """Device reports tunnel port"""
- tunnel_type: str # "ssh" | "dashboard"
- port: Optional[int] = None
- status: str # "connected" | "disconnected"
- async def _auth_device_token(
- authorization: str | None, db: AsyncSession
- ) -> Device:
- """Authenticate device by token from Authorization header."""
- if not authorization or not authorization.lower().startswith("bearer "):
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Missing token",
- )
- token = authorization.split(None, 1)[1]
- result = await db.execute(select(Device).where(Device.device_token == token))
- device = result.scalar_one_or_none()
- if not device:
- raise HTTPException(
- status_code=status.HTTP_401_UNAUTHORIZED,
- detail="Invalid token",
- )
- return device
- @router.post("/tunnel-port")
- async def report_tunnel_port(
- req: TunnelPortRequest,
- db: Annotated[AsyncSession, Depends(get_db)],
- authorization: Annotated[str | None, Header()] = None,
- ):
- """
- Device reports tunnel port allocation after establishing reverse SSH tunnel.
- Args:
- tunnel_type: "ssh" or "dashboard"
- port: Allocated port number (if connected)
- status: "connected" or "disconnected"
- Returns:
- success: True
- """
- device = await _auth_device_token(authorization, db)
- if req.tunnel_type not in ["ssh", "dashboard"]:
- raise HTTPException(
- status_code=status.HTTP_400_BAD_REQUEST,
- detail="tunnel_type must be 'ssh' or 'dashboard'"
- )
- # Update tunnel status
- tunnel_service.report_device_port(
- device_id=device.mac_address,
- tunnel_type=req.tunnel_type,
- port=req.port,
- status=req.status
- )
- print(f"[tunnel] Device {device.mac_address} reported {req.tunnel_type} "
- f"tunnel {req.status}" + (f" on port {req.port}" if req.port else ""))
- return {"success": True}
|