293 lines
9.4 KiB
Python
293 lines
9.4 KiB
Python
|
||
from fastapi import Depends, APIRouter, HTTPException, Depends
|
||
from sqlalchemy.orm import Session
|
||
from app.db import models
|
||
from app.core.security import get_current_user
|
||
from app.api import schemas
|
||
from sqlalchemy import or_, and_, exists
|
||
from sqlalchemy.exc import IntegrityError
|
||
|
||
|
||
# бд
|
||
def get_db():
|
||
db = models.SessionLocal()
|
||
try:
|
||
yield db
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
usersRouter = APIRouter(
|
||
prefix="/users",
|
||
tags=[],
|
||
)
|
||
|
||
# Пример защищенного роута
|
||
|
||
|
||
@usersRouter.get("/me")
|
||
async def read_users_me(current_user: models.User = Depends(get_current_user)):
|
||
return {
|
||
"id": current_user.id,
|
||
"username": current_user.username,
|
||
"first_name": current_user.first_name,
|
||
"last_name": current_user.last_name,
|
||
"phone": current_user.phone,
|
||
"email": getattr(current_user, "email", None),
|
||
"about": current_user.about,
|
||
"public_key": current_user.public_key,
|
||
"encrypted_private_key": current_user.encrypted_private_key,
|
||
}
|
||
|
||
|
||
@usersRouter.put("/me")
|
||
async def update_users_me(
|
||
data: schemas.UpdateMe,
|
||
current_user: models.User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
user_to_update = db.merge(current_user)
|
||
|
||
if data.username is not None:
|
||
user_to_update.username = data.username
|
||
if data.first_name is not None:
|
||
user_to_update.first_name = data.first_name
|
||
if data.last_name is not None:
|
||
user_to_update.last_name = data.last_name
|
||
if data.phone is not None:
|
||
user_to_update.phone = data.phone or None
|
||
if data.email is not None:
|
||
user_to_update.email = data.email or None
|
||
if data.about is not None:
|
||
user_to_update.about = data.about or None
|
||
|
||
try:
|
||
db.commit()
|
||
except IntegrityError:
|
||
db.rollback()
|
||
raise HTTPException(
|
||
status_code=400, detail="phone/email already in use")
|
||
|
||
db.refresh(user_to_update)
|
||
return {
|
||
"status": "ok",
|
||
"user": {
|
||
"id": user_to_update.id,
|
||
"username": user_to_update.username,
|
||
"first_name": user_to_update.first_name,
|
||
"last_name": user_to_update.last_name,
|
||
"phone": user_to_update.phone,
|
||
"email": getattr(user_to_update, "email", None),
|
||
"about": user_to_update.about,
|
||
},
|
||
}
|
||
|
||
|
||
@usersRouter.put("/me/encryption-key")
|
||
async def update_encrypted_private_key(
|
||
data: schemas.UpdateEncryptedPrivateKey,
|
||
current_user: models.User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
user_to_update = db.merge(current_user)
|
||
user_to_update.encrypted_private_key = data.encrypted_private_key
|
||
|
||
try:
|
||
db.commit()
|
||
except Exception:
|
||
db.rollback()
|
||
raise HTTPException(
|
||
status_code=500, detail="Не удалось сохранить ключ шифрования")
|
||
|
||
db.refresh(user_to_update)
|
||
return {"status": "ok"}
|
||
|
||
|
||
@usersRouter.put("/me/password")
|
||
async def change_password(
|
||
data: schemas.ChangePassword,
|
||
current_user: models.User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
from app.core.security import verify_password, get_password_hash
|
||
|
||
if not verify_password(data.current_password, current_user.hashed_password):
|
||
raise HTTPException(status_code=400, detail="Неверный текущий пароль")
|
||
|
||
user_to_update = db.merge(current_user)
|
||
user_to_update.hashed_password = get_password_hash(data.new_password)
|
||
|
||
try:
|
||
db.commit()
|
||
except Exception:
|
||
db.rollback()
|
||
raise HTTPException(
|
||
status_code=500, detail="Не удалось изменить пароль")
|
||
|
||
db.refresh(user_to_update)
|
||
return {"status": "ok"}
|
||
|
||
|
||
@usersRouter.put("/me/privacy")
|
||
async def update_privacy_settings(
|
||
data: schemas.UpdatePrivacySettings,
|
||
current_user: models.User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
user_to_update = db.merge(current_user)
|
||
|
||
if data.show_email is not None:
|
||
user_to_update.show_email = 1 if data.show_email else 0
|
||
if data.show_phone is not None:
|
||
user_to_update.show_phone = 1 if data.show_phone else 0
|
||
if data.show_avatar is not None:
|
||
user_to_update.show_avatar = 1 if data.show_avatar else 0
|
||
if data.show_about is not None:
|
||
user_to_update.show_about = 1 if data.show_about else 0
|
||
if data.show_username is not None:
|
||
user_to_update.show_username = 1 if data.show_username else 0
|
||
|
||
try:
|
||
db.commit()
|
||
except Exception:
|
||
db.rollback()
|
||
raise HTTPException(
|
||
status_code=500, detail="Не удалось сохранить настройки конфиденциальности")
|
||
|
||
db.refresh(user_to_update)
|
||
return {"status": "ok"}
|
||
|
||
|
||
@usersRouter.get("/me/privacy")
|
||
async def get_privacy_settings(current_user: models.User = Depends(get_current_user)):
|
||
"""
|
||
Получить настройки конфиденциальности текущего пользователя.
|
||
"""
|
||
return {
|
||
"show_email": bool(current_user.show_email),
|
||
"show_phone": bool(current_user.show_phone),
|
||
"show_avatar": bool(current_user.show_avatar),
|
||
"show_about": bool(current_user.show_about),
|
||
"show_username": bool(current_user.show_username),
|
||
}
|
||
|
||
|
||
@usersRouter.get("/all")
|
||
async def read_users_all(current_user: models.User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||
users = db.query(models.User).all()
|
||
return [{"id": user.id, "username": user.username, "name": f"{user.first_name} {user.last_name or ''}".strip(), "public_key": user.public_key} for user in users]
|
||
|
||
|
||
@usersRouter.get("/chats")
|
||
async def read_users_chats(
|
||
current_user: models.User = Depends(get_current_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
"""
|
||
Список контактов для экрана чатов: последний месседж + время + непрочитанные.
|
||
|
||
last_message возвращается в том виде, как хранится в БД (зашифрованный content).
|
||
Клиент должен расшифровать превью локально.
|
||
"""
|
||
|
||
|
||
users = (
|
||
db.query(models.User)
|
||
.filter(models.User.id != current_user.id)
|
||
.filter(exists().where(
|
||
or_(
|
||
and_(models.Message.sender_id == current_user.id,
|
||
models.Message.receiver_id == models.User.id),
|
||
and_(models.Message.sender_id == models.User.id,
|
||
models.Message.receiver_id == current_user.id)
|
||
)
|
||
))
|
||
.all()
|
||
)
|
||
|
||
result = []
|
||
for user in users:
|
||
last_msg = (
|
||
db.query(models.Message)
|
||
.filter(
|
||
or_(
|
||
and_(
|
||
models.Message.sender_id == current_user.id,
|
||
models.Message.receiver_id == user.id,
|
||
),
|
||
and_(
|
||
models.Message.sender_id == user.id,
|
||
models.Message.receiver_id == current_user.id,
|
||
),
|
||
)
|
||
)
|
||
.order_by(models.Message.timestamp.desc())
|
||
.first()
|
||
)
|
||
|
||
unread_count = (
|
||
db.query(models.Message)
|
||
.filter(
|
||
models.Message.sender_id == user.id,
|
||
models.Message.receiver_id == current_user.id,
|
||
models.Message.read_at.is_(None),
|
||
)
|
||
.count()
|
||
)
|
||
|
||
result.append(
|
||
{
|
||
"id": user.id,
|
||
"username": user.username,
|
||
"name": f"{user.first_name} {user.last_name or ''}".strip(),
|
||
"public_key": user.public_key,
|
||
"last_message": last_msg.content if last_msg else None,
|
||
"last_message_time": (last_msg.timestamp.isoformat() if last_msg and last_msg.timestamp else None),
|
||
"unread_count": unread_count,
|
||
}
|
||
)
|
||
|
||
result.sort(key=lambda x: x['last_message_time'] or '', reverse=True)
|
||
return result
|
||
|
||
|
||
@usersRouter.get("/{user_id}", response_model=schemas.UserProfile)
|
||
def get_user_by_id(
|
||
user_id: int,
|
||
db: Session = Depends(get_db),
|
||
current_user: models.User = Depends(get_current_user)
|
||
):
|
||
"""
|
||
Получить информацию о пользователе с учетом настроек конфиденциальности.
|
||
"""
|
||
user = db.query(models.User).filter(models.User.id == user_id).first()
|
||
|
||
if not user:
|
||
raise HTTPException(status_code=404, detail="Пользователь не найден")
|
||
|
||
# Возвращаем информацию с учетом настроек конфиденциальности
|
||
profile_data = {
|
||
"id": user.id,
|
||
"public_key": user.public_key,
|
||
}
|
||
|
||
# Проверяем настройки конфиденциальности
|
||
if user.show_username:
|
||
profile_data["username"] = user.username
|
||
|
||
if user.show_avatar:
|
||
# Для аватара пока просто передаем имя, клиент сам сгенерирует аватар
|
||
profile_data["first_name"] = user.first_name
|
||
profile_data["last_name"] = user.last_name
|
||
|
||
if user.show_about:
|
||
profile_data["about"] = user.about
|
||
|
||
if user.show_phone:
|
||
profile_data["phone"] = user.phone
|
||
|
||
if user.show_email:
|
||
profile_data["email"] = user.email
|
||
|
||
return profile_data
|