"""Stations router - CRUD + recipe assignments.""" from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from database import get_db from middleware.api_key import get_current_user, require_admin_user from models.user import User from schemas.station import ( StationCreate, StationUpdate, StationResponse, StationRecipeAssignmentCreate, StationRecipeAssignmentResponse, RecipeSummary, ) from services import station_service router = APIRouter(prefix="/api/stations", tags=["stations"]) @router.get("", response_model=list[StationResponse]) async def list_stations( active_only: bool = False, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): """List all stations (admin only).""" stations = await station_service.list_stations(db, active_only=active_only) return [StationResponse.model_validate(s) for s in stations] @router.post("", response_model=StationResponse, status_code=status.HTTP_201_CREATED) async def create_new_station( data: StationCreate, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): """Create a station (admin only).""" station = await station_service.create_station(db, data, admin) return StationResponse.model_validate(station) # NOTE: this literal-prefix route must stay above the /{station_id} routes. # The int-typed station_id param already guards against "by-code" being # matched as a station id, but keeping the explicit order avoids surprises # during refactors (e.g. if someone regroups handlers by HTTP method). @router.get("/by-code/{code}/recipes", response_model=list[RecipeSummary]) async def list_recipes_by_station_code( code: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """Operator view: active recipes assigned to the station with this code. Used by the Flask client at startup / on select_recipe page. Any authenticated user can call this; filtering is by station code from the client's STATION_CODE environment variable. """ station = await station_service.get_station_by_code(db, code) if station is None or not station.active: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Station not found", ) recipes = await station_service.list_station_recipes(db, station.id) return [RecipeSummary.model_validate(r) for r in recipes] @router.get("/{station_id}", response_model=StationResponse) async def get_single_station( station_id: int, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): """Get a station by id (admin only).""" station = await station_service.get_station(db, station_id) return StationResponse.model_validate(station) @router.put("/{station_id}", response_model=StationResponse) async def update_existing_station( station_id: int, data: StationUpdate, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): """Update a station (admin only).""" station = await station_service.update_station(db, station_id, data) return StationResponse.model_validate(station) @router.delete("/{station_id}", status_code=status.HTTP_204_NO_CONTENT) async def remove_station( station_id: int, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): """Delete a station (admin only). Cascades to assignments.""" await station_service.delete_station(db, station_id) @router.get("/{station_id}/recipes", response_model=list[RecipeSummary]) async def list_assigned_recipes( station_id: int, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): """Admin view: recipes assigned to this station (active only).""" recipes = await station_service.list_station_recipes(db, station_id) return [RecipeSummary.model_validate(r) for r in recipes] @router.post( "/{station_id}/recipes", response_model=StationRecipeAssignmentResponse, status_code=status.HTTP_201_CREATED, ) async def assign_recipe_to_station( station_id: int, data: StationRecipeAssignmentCreate, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): """Assign a recipe to a station (admin only).""" assignment = await station_service.assign_recipe( db, station_id, data.recipe_id, admin, ) return StationRecipeAssignmentResponse.model_validate(assignment) @router.delete( "/{station_id}/recipes/{recipe_id}", status_code=status.HTTP_204_NO_CONTENT, ) async def unassign_recipe_from_station( station_id: int, recipe_id: int, admin: User = Depends(require_admin_user), db: AsyncSession = Depends(get_db), ): """Remove a recipe assignment (admin only).""" await station_service.unassign_recipe(db, station_id, recipe_id)