"""Satellite management routes (dashboard side). The plaintext API key is returned exactly once on creation. """ from fastapi import APIRouter, Depends, Request from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import client_ip, get_current_user from app.core.database import get_db from app.core.exceptions import NotFoundError from app.models.customer import Customer from app.models.satellite import Satellite, generate_api_key, hash_api_key from app.models.user import User from app.schemas.satellite import SatelliteCreate, SatelliteCreated, SatelliteRead from app.services.audit import AuditService router = APIRouter() def _created_response(satellite: Satellite, api_key: str) -> SatelliteCreated: data = SatelliteRead.model_validate(satellite).model_dump() return SatelliteCreated(**data, api_key=api_key) @router.get("", response_model=list[SatelliteRead]) async def list_satellites( customer_id: int | None = None, db: AsyncSession = Depends(get_db), _user: User = Depends(get_current_user), ) -> list[Satellite]: stmt = select(Satellite).order_by(Satellite.id) if customer_id is not None: stmt = stmt.where(Satellite.customer_id == customer_id) result = await db.execute(stmt) return list(result.scalars().all()) @router.post("", response_model=SatelliteCreated, status_code=201) async def create_satellite( payload: SatelliteCreate, request: Request, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ) -> SatelliteCreated: customer = await db.get(Customer, payload.customer_id) if not customer: raise NotFoundError("Kunde nicht gefunden") api_key = generate_api_key() satellite = Satellite( customer_id=payload.customer_id, name=payload.name, api_key_hash=hash_api_key(api_key), api_key_prefix=api_key[:11], ) db.add(satellite) await db.flush() await AuditService(db).log( username=user.username, action="satellite.create", target=f"{customer.name}/{satellite.name}", customer_id=customer.id, ip_address=client_ip(request), ) return _created_response(satellite, api_key) @router.delete("/{satellite_id}", status_code=204) async def delete_satellite( satellite_id: int, request: Request, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ) -> None: satellite = await db.get(Satellite, satellite_id) if not satellite: raise NotFoundError("Satellite nicht gefunden") await AuditService(db).log( username=user.username, action="satellite.delete", target=satellite.name, customer_id=satellite.customer_id, ip_address=client_ip(request), ) await db.delete(satellite) @router.post("/{satellite_id}/rotate-key", response_model=SatelliteCreated) async def rotate_satellite_key( satellite_id: int, request: Request, db: AsyncSession = Depends(get_db), user: User = Depends(get_current_user), ) -> SatelliteCreated: satellite = await db.get(Satellite, satellite_id) if not satellite: raise NotFoundError("Satellite nicht gefunden") api_key = generate_api_key() satellite.api_key_hash = hash_api_key(api_key) satellite.api_key_prefix = api_key[:11] await AuditService(db).log( username=user.username, action="satellite.rotate_key", target=satellite.name, customer_id=satellite.customer_id, ip_address=client_ip(request), ) return _created_response(satellite, api_key)