import secrets import string from datetime import UTC, datetime, timedelta import jwt from argon2 import PasswordHasher from argon2.exceptions import InvalidHashError, VerifyMismatchError from app.config import get_settings COOKIE_NAME = "pgames_session" _hasher = PasswordHasher() _ALPHABET = string.ascii_letters + string.digits def hash_password(password: str) -> str: return _hasher.hash(password) def verify_password(password_hash: str, password: str) -> bool: try: return _hasher.verify(password_hash, password) except (VerifyMismatchError, InvalidHashError): return False def generate_password(length: int = 14) -> str: return "".join(secrets.choice(_ALPHABET) for _ in range(length)) def create_token(user_id: int, token_version: int) -> str: settings = get_settings() payload = { "sub": str(user_id), "tv": token_version, "exp": datetime.now(UTC) + timedelta(hours=settings.session_ttl_hours), } return jwt.encode(payload, settings.secret_key, algorithm="HS256") def decode_token(token: str) -> tuple[int, int] | None: try: payload = jwt.decode(token, get_settings().secret_key, algorithms=["HS256"]) return int(payload["sub"]), int(payload["tv"]) except (jwt.PyJWTError, KeyError, ValueError): return None def validate_new_password(password: str) -> str | None: if len(password) < 8: return "Пароль должен быть не короче 8 символов" return None def validate_username(username: str) -> str | None: if not 3 <= len(username) <= 64: return "Логин должен быть от 3 до 64 символов" if not all(c.isalnum() or c in "._-" for c in username): return "Логин может содержать буквы, цифры и . _ -" return None