Files
vpn-control-panel/app/api/deps.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

141 lines
3.6 KiB
Python

from collections.abc import AsyncGenerator
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import async_session_factory
from app.repositories import (
UserRepository,
PaymentRepository,
ServerRepository,
TariffRepository,
NotificationRepository,
AdminRepository,
)
from app.services import (
UserService,
PaymentService,
BillingService,
NotificationService,
ServerService,
VPNService,
)
from app.services.auth import AuthService
security = HTTPBearer()
async def get_db() -> AsyncGenerator[AsyncSession, None]:
async with async_session_factory() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
def get_user_service(db: AsyncSession = Depends(get_db)) -> UserService:
return UserService(UserRepository(db))
def get_payment_service(db: AsyncSession = Depends(get_db)) -> PaymentService:
return PaymentService(
PaymentRepository(db),
UserRepository(db),
TariffRepository(db),
)
def get_billing_service(db: AsyncSession = Depends(get_db)) -> BillingService:
return BillingService(
PaymentRepository(db),
TariffRepository(db),
UserRepository(db),
)
def get_notification_service(
db: AsyncSession = Depends(get_db),
) -> NotificationService:
return NotificationService(
NotificationRepository(db),
UserRepository(db),
)
def get_server_service(db: AsyncSession = Depends(get_db)) -> ServerService:
return ServerService(ServerRepository(db))
def get_vpn_service(db: AsyncSession = Depends(get_db)) -> VPNService:
return VPNService(
ServerRepository(db),
UserRepository(db),
)
def get_tariff_repo(db: AsyncSession = Depends(get_db)) -> TariffRepository:
return TariffRepository(db)
def get_server_repo(db: AsyncSession = Depends(get_db)) -> ServerRepository:
return ServerRepository(db)
def get_auth_service(db: AsyncSession = Depends(get_db)) -> AuthService:
return AuthService(AdminRepository(db))
async def get_current_admin(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: AsyncSession = Depends(get_db),
) -> dict:
from app.services.auth import AuthService
auth_svc = AuthService(AdminRepository(db))
try:
payload = auth_svc.decode_token(credentials.credentials)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=str(e),
)
if payload.get("type") != "access":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token type",
)
telegram_id = int(payload["sub"])
admin = await AdminRepository(db).get_by_telegram_id(telegram_id)
if admin is None or not admin.is_active:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Admin not found or deactivated",
)
return {
"admin_id": admin.id,
"telegram_id": admin.telegram_id,
"role": admin.role.value,
"username": admin.username,
}
def require_admin(payload: dict = Depends(get_current_admin)) -> dict:
return payload
def require_superadmin(payload: dict = Depends(get_current_admin)) -> dict:
if payload["role"] != "superadmin":
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Superadmin role required",
)
return payload