Refactored:

- пофикшен баг с авторизацией;
- поменен README.md, более подробно описан запуск проекта;
- починен .env для проекта.
Checked:
- docker-compose работает;
- auth работает;
- чат с нейросетью работает, но кидает 400 из за NEWPLANET-AI-AGENTS,нужно настроить подключение.
This commit is contained in:
10 changed files with 103 additions and 33 deletions

View File

@@ -1,20 +1,54 @@
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from jose import JWTError, jwt
from passlib.context import CryptContext
import bcrypt
from app.core.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Проверка пароля"""
return pwd_context.verify(plain_password, hashed_password)
# Используем bcrypt напрямую для проверки
try:
return bcrypt.checkpw(plain_password.encode('utf-8'), hashed_password.encode('utf-8'))
except (ValueError, TypeError, AttributeError):
return False
def _truncate_password_to_72_bytes(password: str) -> str:
"""Обрезает пароль до 72 байт, корректно обрабатывая UTF-8"""
password_bytes = password.encode('utf-8')
if len(password_bytes) <= 72:
return password
# Обрезаем до 72 байт
password_bytes = password_bytes[:72]
# Удаляем неполные UTF-8 последовательности в конце
# (байты, которые начинаются с 10xxxxxx, но не являются началом символа)
while password_bytes and (password_bytes[-1] & 0xC0) == 0x80:
password_bytes = password_bytes[:-1]
return password_bytes.decode('utf-8', errors='replace')
def get_password_hash(password: str) -> str:
"""Хеширование пароля"""
return pwd_context.hash(password)
# bcrypt имеет ограничение в 72 байта
# Обрезаем пароль до 72 байт перед хешированием
password_bytes = password.encode('utf-8')
if len(password_bytes) > 72:
# Обрезаем до 72 байт
password_bytes = password_bytes[:72]
# Удаляем неполные UTF-8 последовательности в конце
while password_bytes and (password_bytes[-1] & 0xC0) == 0x80:
password_bytes = password_bytes[:-1]
password = password_bytes.decode('utf-8', errors='replace')
password_bytes = password.encode('utf-8')
# Используем bcrypt напрямую, чтобы избежать проблем с инициализацией passlib
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password_bytes, salt)
return hashed.decode('utf-8')
def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:

View File

@@ -1,6 +1,5 @@
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, DateTime, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import Column, DateTime, func, String
import uuid
Base = declarative_base()
@@ -11,7 +10,7 @@ class BaseModel(Base):
__abstract__ = True
id = Column(
UUID(as_uuid=False),
String,
primary_key=True,
default=lambda: str(uuid.uuid4()),
nullable=False

View File

@@ -1,4 +1,4 @@
from sqlalchemy import Column, String, Integer, ForeignKey, Text
from sqlalchemy import Column, String, Integer, ForeignKey, Text, Boolean
from sqlalchemy.orm import relationship
from app.db.base import BaseModel

View File

@@ -11,7 +11,7 @@ class UserBase(BaseModel):
class UserCreate(UserBase):
password: str = Field(..., min_length=8)
password: str = Field(..., min_length=8, max_length=72, description="Password must be between 8 and 72 characters")
class UserUpdate(BaseModel):

View File

@@ -1,4 +1,5 @@
import aiohttp
import ssl
import base64
import uuid
import time
@@ -30,7 +31,13 @@ class GigaChatService:
data = {"scope": "GIGACHAT_API_PERS"}
async with aiohttp.ClientSession() as session:
# Создаем SSL контекст без проверки сертификата (только для разработки!)
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
connector = aiohttp.TCPConnector(ssl=ssl_context)
async with aiohttp.ClientSession(connector=connector) as session:
async with session.post(
settings.GIGACHAT_AUTH_URL,
headers=headers,
@@ -75,7 +82,13 @@ class GigaChatService:
"max_tokens": 2000
}
async with aiohttp.ClientSession() as session:
# Создаем SSL контекст без проверки сертификата (только для разработки!)
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
connector = aiohttp.TCPConnector(ssl=ssl_context)
async with aiohttp.ClientSession(connector=connector) as session:
async with session.post(
f"{settings.GIGACHAT_BASE_URL}/chat/completions",
headers=headers,