"""Admin endpoints для управления weight profiles. Per #114 (Макс feedback: садики ≠ Мегамарт). Auth: gendsgn.ru-wide Caddy basic_auth gate (PR #426). App-level X-Admin-Token header removed 2026-05-23 — двойная auth избыточна для pilot. GET /api/v1/admin/site-finder/weight-profiles?user_id=... → list POST /api/v1/admin/site-finder/weight-profiles → create GET /api/v1/admin/site-finder/weight-profiles/{id}?user_id= → get one PUT /api/v1/admin/site-finder/weight-profiles/{id}?user_id= → update DELETE /api/v1/admin/site-finder/weight-profiles/{id}?user_id= → delete """ from __future__ import annotations from typing import Annotated, Any from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.orm import Session from app.core.db import get_db from app.services.site_finder.weight_profiles import ( WeightProfile, WeightProfileCreate, WeightProfileUpdate, create_profile, delete_profile, get_profile, list_profiles, list_profiles_with_system, update_profile, ) router = APIRouter() @router.get("", response_model=list[WeightProfile]) def list_user_profiles( user_id: Annotated[str, Query(min_length=1, description="user_id для фильтра профилей")], db: Annotated[Session, Depends(get_db)], include_system: Annotated[ bool, Query(description="Включить системные preset-профили (Эконом/Комфорт/Бизнес)"), ] = False, ) -> Any: """Список всех weight profiles для заданного user_id. Default-профиль первым. При include_system=true добавляются системные presets (user_id='__system__'): Эконом, Комфорт, Бизнес — готовые шаблоны весов POI. """ if include_system: return list_profiles_with_system(db, user_id) return list_profiles(db, user_id) @router.post("", response_model=WeightProfile, status_code=status.HTTP_201_CREATED) def create_user_profile( payload: WeightProfileCreate, db: Annotated[Session, Depends(get_db)], ) -> Any: """Создать новый weight profile. При is_default=True снимает is_default у всех существующих профилей пользователя в одной транзакции. """ return create_profile(db, payload) @router.get("/{profile_id}", response_model=WeightProfile) def get_user_profile( profile_id: int, user_id: Annotated[str, Query(min_length=1, description="Владелец профиля")], db: Annotated[Session, Depends(get_db)], ) -> Any: """Получить один weight profile по id (scoped к user_id).""" profile = get_profile(db, user_id, profile_id) if profile is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found") return profile @router.put("/{profile_id}", response_model=WeightProfile) def update_user_profile( profile_id: int, user_id: Annotated[str, Query(min_length=1, description="Владелец профиля")], payload: WeightProfileUpdate, db: Annotated[Session, Depends(get_db)], ) -> Any: """PATCH-style обновление weight profile. Передавай только изменяемые поля. Не переданные поля остаются прежними. При is_default=True снимает is_default у остальных профилей пользователя. """ profile = update_profile(db, user_id, profile_id, payload) if profile is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found") return profile @router.delete("/{profile_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_user_profile( profile_id: int, user_id: Annotated[str, Query(min_length=1, description="Владелец профиля")], db: Annotated[Session, Depends(get_db)], ) -> None: """Удалить weight profile. 404 если не найден.""" deleted = delete_profile(db, user_id, profile_id) if not deleted: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found")