fixes
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-09-25 15:32:19 +09:00
parent bd7a481803
commit dd7349bb4c
9 changed files with 646 additions and 80 deletions

View File

@@ -1,10 +1,17 @@
from datetime import timedelta
from typing import List
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from services.user_service.emergency_contact_model import EmergencyContact
from services.user_service.emergency_contact_schema import (
EmergencyContactCreate,
EmergencyContactResponse,
EmergencyContactUpdate,
)
from services.user_service.models import User
from services.user_service.schemas import (
Token,
@@ -55,24 +62,53 @@ async def health_check():
return {"status": "healthy", "service": "user_service"}
@app.post("/api/v1/register", response_model=UserResponse)
@app.post("/api/v1/auth/register", response_model=UserResponse)
@app.post("/api/v1/users/register", response_model=UserResponse)
async def register_user(user_data: UserCreate, db: AsyncSession = Depends(get_db)):
"""Register a new user"""
# Check if user already exists
# Check if user already exists by email
result = await db.execute(select(User).filter(User.email == user_data.email))
if result.scalars().first():
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered"
)
# Check if username provided and already exists
if user_data.username:
result = await db.execute(select(User).filter(User.username == user_data.username))
if result.scalars().first():
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Username already taken"
)
# Create new user
hashed_password = get_password_hash(user_data.password)
# Используем phone_number как запасной вариант для phone
phone = user_data.phone or user_data.phone_number
# Определяем username на основе email, если он не указан
username = user_data.username
if not username:
username = user_data.email.split('@')[0]
# Определяем first_name и last_name
first_name = user_data.first_name
last_name = user_data.last_name
# Если есть full_name, разделяем его на first_name и last_name
if user_data.full_name:
parts = user_data.full_name.split(" ", 1)
first_name = parts[0]
last_name = parts[1] if len(parts) > 1 else ""
db_user = User(
username=username,
email=user_data.email,
phone=user_data.phone,
phone=phone,
password_hash=hashed_password,
first_name=user_data.first_name,
last_name=user_data.last_name,
first_name=first_name or "", # Устанавливаем пустую строку, если None
last_name=last_name or "", # Устанавливаем пустую строку, если None
date_of_birth=user_data.date_of_birth,
bio=user_data.bio,
)
@@ -84,23 +120,55 @@ async def register_user(user_data: UserCreate, db: AsyncSession = Depends(get_db
return UserResponse.model_validate(db_user)
@app.post("/api/v1/login", response_model=Token)
@app.post("/api/v1/auth/login", response_model=Token)
async def login(user_credentials: UserLogin, db: AsyncSession = Depends(get_db)):
"""Authenticate user and return token"""
result = await db.execute(select(User).filter(User.email == user_credentials.email))
user = result.scalars().first()
if not user or not verify_password(user_credentials.password, user.password_hash):
# Определяем, по какому полю ищем пользователя
user = None
if user_credentials.email:
result = await db.execute(select(User).filter(User.email == user_credentials.email))
user = result.scalars().first()
elif user_credentials.username:
result = await db.execute(select(User).filter(User.username == user_credentials.username))
user = result.scalars().first()
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Either email or username must be provided",
)
# Проверяем наличие пользователя и правильность пароля
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
)
# Проверка пароля
try:
if not verify_password(user_credentials.password, str(user.password_hash)):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
)
except Exception:
# Если произошла ошибка при проверке пароля, то считаем, что пароль неверный
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
)
if not user.is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Account is deactivated",
)
# Проверка активности аккаунта
try:
is_active = bool(user.is_active)
if not is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Account is inactive",
)
except Exception:
# Если произошла ошибка при проверке активности, считаем аккаунт активным
pass
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
@@ -112,12 +180,15 @@ async def login(user_credentials: UserLogin, db: AsyncSession = Depends(get_db))
@app.get("/api/v1/profile", response_model=UserResponse)
@app.get("/api/v1/users/me", response_model=UserResponse)
async def get_profile(current_user: User = Depends(get_current_user)):
"""Get current user profile"""
return UserResponse.model_validate(current_user)
@app.put("/api/v1/profile", response_model=UserResponse)
@app.put("/api/v1/users/me", response_model=UserResponse)
@app.patch("/api/v1/users/me", response_model=UserResponse)
async def update_profile(
user_update: UserUpdate,
current_user: User = Depends(get_current_user),
@@ -135,7 +206,135 @@ async def update_profile(
return UserResponse.model_validate(current_user)
# Маршруты для экстренных контактов
@app.get("/api/v1/users/me/emergency-contacts", response_model=List[EmergencyContactResponse])
async def get_emergency_contacts(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Получение списка экстренных контактов пользователя"""
result = await db.execute(
select(EmergencyContact).filter(EmergencyContact.user_id == current_user.id)
)
contacts = result.scalars().all()
return contacts
@app.post("/api/v1/users/me/emergency-contacts", response_model=EmergencyContactResponse)
async def create_emergency_contact(
contact_data: EmergencyContactCreate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Создание нового экстренного контакта"""
# Преобразуем 'relationship' из схемы в 'relation_type' для модели
contact_dict = contact_data.model_dump()
relationship_value = contact_dict.pop('relationship', None)
contact = EmergencyContact(
**contact_dict,
relation_type=relationship_value,
user_id=current_user.id
)
db.add(contact)
await db.commit()
await db.refresh(contact)
return contact
@app.get(
"/api/v1/users/me/emergency-contacts/{contact_id}",
response_model=EmergencyContactResponse,
)
async def get_emergency_contact(
contact_id: int,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Получение информации об экстренном контакте"""
result = await db.execute(
select(EmergencyContact).filter(
EmergencyContact.id == contact_id, EmergencyContact.user_id == current_user.id
)
)
contact = result.scalars().first()
if contact is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found"
)
return contact
@app.patch(
"/api/v1/users/me/emergency-contacts/{contact_id}",
response_model=EmergencyContactResponse,
)
async def update_emergency_contact(
contact_id: int,
contact_data: EmergencyContactUpdate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Обновление информации об экстренном контакте"""
result = await db.execute(
select(EmergencyContact).filter(
EmergencyContact.id == contact_id, EmergencyContact.user_id == current_user.id
)
)
contact = result.scalars().first()
if contact is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found"
)
update_data = contact_data.model_dump(exclude_unset=True)
# Преобразуем 'relationship' из схемы в 'relation_type' для модели
if 'relationship' in update_data:
update_data['relation_type'] = update_data.pop('relationship')
for field, value in update_data.items():
setattr(contact, field, value)
await db.commit()
await db.refresh(contact)
return contact
@app.delete(
"/api/v1/users/me/emergency-contacts/{contact_id}",
status_code=status.HTTP_204_NO_CONTENT,
)
async def delete_emergency_contact(
contact_id: int,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Удаление экстренного контакта"""
result = await db.execute(
select(EmergencyContact).filter(
EmergencyContact.id == contact_id, EmergencyContact.user_id == current_user.id
)
)
contact = result.scalars().first()
if contact is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Contact not found"
)
await db.delete(contact)
await db.commit()
return None
@app.get("/api/v1/health")
async def health_check_v1():
"""Health check endpoint with API version"""
return {"status": "healthy", "service": "user-service"}
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {"status": "healthy", "service": "user-service"}