from datetime import datetime, timedelta, timezone from jose import JWTError, jwt from loguru import logger from app.models.admin import Admin from app.repositories.admin import AdminRepository from app.settings import settings class AuthService: def __init__(self, admin_repo: AdminRepository): self.admin_repo = admin_repo def create_access_token(self, telegram_id: int, role: str) -> str: now = datetime.now(timezone.utc) payload = { "sub": str(telegram_id), "role": role, "type": "access", "iat": int(now.timestamp()), "exp": int( (now + timedelta(minutes=settings.jwt_access_expire_minutes)).timestamp() ), } return jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm) def create_refresh_token(self, telegram_id: int) -> str: now = datetime.now(timezone.utc) payload = { "sub": str(telegram_id), "type": "refresh", "iat": int(now.timestamp()), "exp": int( (now + timedelta(days=settings.jwt_refresh_expire_days)).timestamp() ), } return jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm) def decode_token(self, token: str) -> dict: try: payload = jwt.decode( token, settings.jwt_secret, algorithms=[settings.jwt_algorithm] ) return payload except JWTError as e: logger.warning("JWT decode failed: {}", e) raise ValueError("Invalid or expired token") from e async def authenticate(self, telegram_id: int, secret_key: str) -> dict | None: if secret_key != settings.jwt_secret: logger.warning("Auth failed: invalid secret key for tg={}", telegram_id) return None admin = await self.admin_repo.get_by_telegram_id(telegram_id) if admin is None: logger.warning("Auth failed: admin not found tg={}", telegram_id) return None if not admin.is_active: logger.warning("Auth failed: admin deactivated tg={}", telegram_id) return None access_token = self.create_access_token(telegram_id, admin.role.value) refresh_token = self.create_refresh_token(telegram_id) logger.info("Admin logged in: tg={} role={}", telegram_id, admin.role.value) return { "access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer", } def refresh_access_token(self, refresh_token: str) -> dict: payload = self.decode_token(refresh_token) if payload.get("type") != "refresh": raise ValueError("Invalid token type") telegram_id = int(payload["sub"]) role = payload.get("role", "moderator") new_access = self.create_access_token(telegram_id, role) return { "access_token": new_access, "refresh_token": refresh_token, "token_type": "bearer", }