- 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
72 lines
2.2 KiB
Python
72 lines
2.2 KiB
Python
from typing import Any, Generic, TypeVar
|
|
|
|
from sqlalchemy import select, func
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.database import Base
|
|
|
|
ModelType = TypeVar("ModelType", bound=Base)
|
|
|
|
|
|
class BaseRepository(Generic[ModelType]):
|
|
def __init__(self, session: AsyncSession, model: type[ModelType]):
|
|
self.session = session
|
|
self.model = model
|
|
|
|
async def create(self, **kwargs: Any) -> ModelType:
|
|
instance = self.model(**kwargs)
|
|
self.session.add(instance)
|
|
await self.session.flush()
|
|
return instance
|
|
|
|
async def get(self, id: int) -> ModelType | None:
|
|
return await self.session.get(self.model, id)
|
|
|
|
async def get_all(
|
|
self,
|
|
*,
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
**filters: Any,
|
|
) -> list[ModelType]:
|
|
stmt = select(self.model)
|
|
|
|
for field_name, value in filters.items():
|
|
if value is not None:
|
|
column = getattr(self.model, field_name, None)
|
|
if column is not None:
|
|
stmt = stmt.where(column == value)
|
|
|
|
stmt = stmt.offset(skip).limit(limit)
|
|
result = await self.session.execute(stmt)
|
|
return list(result.scalars().all())
|
|
|
|
async def update(self, id: int, **kwargs: Any) -> ModelType | None:
|
|
instance = await self.get(id)
|
|
if instance is None:
|
|
return None
|
|
for field, value in kwargs.items():
|
|
setattr(instance, field, value)
|
|
await self.session.flush()
|
|
return instance
|
|
|
|
async def delete(self, id: int) -> bool:
|
|
instance = await self.get(id)
|
|
if instance is None:
|
|
return False
|
|
await self.session.delete(instance)
|
|
await self.session.flush()
|
|
return True
|
|
|
|
async def count(self, **filters: Any) -> int:
|
|
stmt = select(func.count(self.model.id))
|
|
|
|
for field_name, value in filters.items():
|
|
if value is not None:
|
|
column = getattr(self.model, field_name, None)
|
|
if column is not None:
|
|
stmt = stmt.where(column == value)
|
|
|
|
result = await self.session.execute(stmt)
|
|
return result.scalar_one()
|