Files
vpn-control-panel/app/services/auth.py
smolkik-code 7c7c88621d Initial commit: VPN Control Panel
- FastAPI REST API with JWT auth
- aiogram 3 Telegram bot with admin middleware
- APScheduler daily tasks (expiry, reminders, revoke, sync)
- SQLAlchemy 2 async ORM with Alembic migrations
- Jinja2 admin panel (Dashboard, Users, Payments, Servers, Tariffs)
- VPN provider abstraction with MockProvider
- Stats service with revenue/subscription analytics
- Docker Compose (PostgreSQL + Redis + app)
- Healthcheck endpoint
2026-07-05 17:50:11 +07:00

91 lines
3.1 KiB
Python

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",
}