Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added app/cache/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions app/cache/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import redis

from app.core.config import settings


redis_client = redis.Redis.from_url(
settings.redis_url,
decode_responses=True,
)
20 changes: 20 additions & 0 deletions app/cache/keys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class RedisKeys:
"""
Centralized Redis key generation.
"""

@staticmethod
def user_email(email: str) -> str:
return f"user:email:{email}"

@staticmethod
def user_username(username: str) -> str:
return f"user:username:{username}"

@staticmethod
def blacklist(jti: str) -> str:
return f"blacklist:{jti}"

@staticmethod
def session(user_id: str) -> str:
return f"session:{user_id}"
81 changes: 81 additions & 0 deletions app/cache/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from typing import Optional

from app.cache.client import redis_client
import json
from app.cache.keys import RedisKeys


class CacheService:
"""
Redis cache wrapper.
"""

def set(
self,
key: str,
value: str,
ttl: Optional[int] = None,
) -> None:
redis_client.set(
key,
value,
ex=ttl,
)

def get(
self,
key: str,
) -> Optional[str]:
return redis_client.get(key)

def delete(
self,
key: str,
) -> None:
redis_client.delete(key)

def exists(
self,
key: str,
) -> bool:
return bool(redis_client.exists(key))

def set_json(
self,
key: str,
value: dict,
ttl: int = 300,
) -> None:
redis_client.set(
key,
json.dumps(value),
ex=ttl,
)

def get_json(
self,
key: str,
) -> dict | None:
value = redis_client.get(key)

if value is None:
return None

return json.loads(value)

def blacklist_token(
self,
jti: str,
ttl: int,
):
self.set(
RedisKeys.blacklist(jti),
"1",
ttl,
)

def is_blacklisted(
self,
jti: str,
) -> bool:
return self.exists(RedisKeys.blacklist(jti))
19 changes: 19 additions & 0 deletions app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ class Settings(BaseSettings):
# =========================
log_level: str

# =========================
# Redis
# =========================

redis_host: str
redis_port: int
redis_db: int
redis_password: str | None = None

model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
Expand Down Expand Up @@ -79,6 +88,16 @@ def sqlalchemy_database_url(self) -> str:
f"/{self.db_name}"
)

@property
def redis_url(self) -> str:
if self.redis_password:
return (
f"redis://:{self.redis_password}"
f"@{self.redis_host}:{self.redis_port}/{self.redis_db}"
)

return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}"


@lru_cache
def get_settings() -> Settings:
Expand Down
1 change: 1 addition & 0 deletions app/core/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def create_access_token(
"sub": subject,
"exp": expire,
"type": "access",
"jti": str(uuid4()),
}

return jwt.encode(
Expand Down
33 changes: 33 additions & 0 deletions app/modules/auth/dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from fastapi import Depends
from sqlalchemy.orm import Session

from app.db.session import get_db

from app.modules.auth.repository import (
RefreshTokenRepository,
PasswordResetRepository,
EmailVerificationRepository,
)

from app.modules.auth.service import AuthService

from app.modules.roles.repository import RoleRepository
from app.modules.users.repository import UserRepository
from app.providers.factory import get_email_provider


def get_auth_service(
db: Session = Depends(get_db),
) -> AuthService:
"""
Dependency provider for AuthService.
"""

return AuthService(
user_repository=UserRepository(db),
role_repository=RoleRepository(db),
refresh_token_repository=RefreshTokenRepository(db),
password_reset_repository=PasswordResetRepository(db),
email_verification_repository=EmailVerificationRepository(db),
email_provider=get_email_provider(),
)
Loading
Loading