This commit is contained in:
@@ -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"}
|
||||
|
||||
Reference in New Issue
Block a user