gendesign/backend/app/api/v1/admin_weight_profiles.py
lekss361 82217b7edd refactor(security): убрать X-Admin-Token из 5 admin файлов (43 endpoints)
Caddy basic_auth (PR #426) даёт network-level защиту для всего /admin/*.
Двойная auth избыточна для pilot — app-level AdminTokenAuth dep удалён.

Что изменено:
- admin_scrape.py (24 endpoints), admin_cadastre.py (5), admin_jobs.py (3),
  admin_leads.py (6), admin_weight_profiles.py (5): удалён import AdminTokenAuth
  и параметр `_: AdminTokenAuth,` из каждой функции-handler'а
- backend/.env.example: SCRAPE_ADMIN_TOKEN помечен DEPRECATED с инструкцией rollback

Что НЕ менялось:
- core/deps.py: verify_admin_token + AdminTokenAuth остаются (dead code, rollback safety)
- frontend: заголовок X-Admin-Token продолжает слаться, backend игнорирует
- .env.runtime / settings: SCRAPE_ADMIN_TOKEN env var сохранён
2026-05-23 13:29:58 +03:00

110 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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")