feat: auth & admin

This commit is contained in:
2026-06-03 10:40:00 +03:00
parent 4bca90a50e
commit 93199a3095
34 changed files with 1634 additions and 119 deletions
@@ -0,0 +1,61 @@
"""Refresh-token repository — adapter implementing
``app.domain.ports.RefreshTokenRepository``.
"""
import datetime as dt
import uuid
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession
from app.infrastructure.db.models import RefreshTokenModel
class SqlAlchemyRefreshTokenRepository:
def __init__(self, session: AsyncSession) -> None:
self._session = session
async def add(
self,
*,
jti: uuid.UUID,
user_id: uuid.UUID,
token_hash: str,
expires_at: dt.datetime,
) -> None:
self._session.add(
RefreshTokenModel(
jti=jti,
user_id=user_id,
token_hash=token_hash,
expires_at=expires_at,
)
)
await self._session.flush()
async def is_valid(self, jti: uuid.UUID) -> bool:
row = (
await self._session.execute(
select(RefreshTokenModel).where(RefreshTokenModel.jti == jti)
)
).scalar_one_or_none()
if row is None or row.revoked_at is not None:
return False
return row.expires_at > dt.datetime.now(dt.UTC)
async def revoke(self, jti: uuid.UUID) -> None:
await self._session.execute(
update(RefreshTokenModel)
.where(RefreshTokenModel.jti == jti, RefreshTokenModel.revoked_at.is_(None))
.values(revoked_at=dt.datetime.now(dt.UTC))
)
async def revoke_all_for_user(self, user_id: uuid.UUID) -> None:
await self._session.execute(
update(RefreshTokenModel)
.where(
RefreshTokenModel.user_id == user_id,
RefreshTokenModel.revoked_at.is_(None),
)
.values(revoked_at=dt.datetime.now(dt.UTC))
)