#!/usr/bin/env bash set -euo pipefail # 1) Репозиторий: приводить user_id к uuid.UUID cat > services/profiles/src/app/repositories/profile_repository.py <<'PY' import uuid from typing import Optional from sqlalchemy import select from sqlalchemy.orm import Session from app.models.profile import Profile from app.schemas.profile import ProfileCreate class ProfileRepository: def __init__(self, db: Session): self.db = db @staticmethod def _to_uuid(v) -> uuid.UUID: if isinstance(v, uuid.UUID): return v return uuid.UUID(str(v)) def get_by_user(self, user_id) -> Optional[Profile]: uid = self._to_uuid(user_id) stmt = select(Profile).where(Profile.user_id == uid) return self.db.execute(stmt).scalar_one_or_none() def create(self, user_id, obj: ProfileCreate) -> Profile: uid = self._to_uuid(user_id) p = Profile( user_id=uid, gender=obj.gender, city=obj.city, languages=obj.languages or [], interests=obj.interests or [], ) self.db.add(p) self.db.commit() self.db.refresh(p) return p PY # 2) Схемы: дефолты - пустые списки (чтобы не было None → JSONB) cat > services/profiles/src/app/schemas/profile.py <<'PY' from __future__ import annotations from typing import Optional, List from pydantic import BaseModel, Field class ProfileBase(BaseModel): gender: str city: str languages: List[str] = Field(default_factory=list) interests: List[str] = Field(default_factory=list) class ProfileCreate(ProfileBase): pass class ProfileOut(ProfileBase): id: str user_id: str class Config: from_attributes = True PY # 3) Роут: ловим ошибки явно → 400 вместо 500 cat > services/profiles/src/app/api/routes/profiles.py <<'PY' from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from sqlalchemy.exc import IntegrityError, DataError from app.db.deps import get_db from app.schemas.profile import ProfileCreate, ProfileOut from app.services.profile_service import ProfileService from app.core.security import get_current_user # возвращает объект с полями sub, email, role router = APIRouter(prefix="/v1/profiles", tags=["profiles"]) @router.get("/me", response_model=ProfileOut) def get_my_profile(db: Session = Depends(get_db), user=Depends(get_current_user)): svc = ProfileService(db) p = svc.get_by_user(user.sub) if not p: # 404, если профиль отсутствует raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Profile not found") return p @router.post("", response_model=ProfileOut, status_code=status.HTTP_201_CREATED) def create_profile(payload: ProfileCreate, db: Session = Depends(get_db), user=Depends(get_current_user)): svc = ProfileService(db) try: existing = svc.get_by_user(user.sub) if existing: return existing p = svc.create(user.sub, payload) return p except (IntegrityError, DataError, ValueError) as exc: db.rollback() raise HTTPException(status_code=400, detail=f"Invalid data: {exc}") PY # 4) Сервис — тонкая обёртка над репозиторием cat > services/profiles/src/app/services/profile_service.py <<'PY' from sqlalchemy.orm import Session from app.repositories.profile_repository import ProfileRepository from app.schemas.profile import ProfileCreate class ProfileService: def __init__(self, db: Session): self.repo = ProfileRepository(db) def get_by_user(self, user_id): return self.repo.get_by_user(user_id) def create(self, user_id, obj: ProfileCreate): return self.repo.create(user_id, obj) PY echo "[profiles] rebuilding..." docker compose build profiles docker compose restart profiles