chore: update project structure and add testing framework

This commit is contained in:
Davidson Gomes
2025-04-28 20:41:10 -03:00
parent 7af234ef48
commit e7e030dfd5
49 changed files with 1261 additions and 619 deletions

View File

@@ -3,23 +3,26 @@ import os
import sys
from src.config.settings import settings
class CustomFormatter(logging.Formatter):
"""Custom formatter for logs"""
grey = "\x1b[38;20m"
yellow = "\x1b[33;20m"
red = "\x1b[31;20m"
bold_red = "\x1b[31;1m"
reset = "\x1b[0m"
format_template = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
format_template = (
"%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
)
FORMATS = {
logging.DEBUG: grey + format_template + reset,
logging.INFO: grey + format_template + reset,
logging.WARNING: yellow + format_template + reset,
logging.ERROR: red + format_template + reset,
logging.CRITICAL: bold_red + format_template + reset
logging.CRITICAL: bold_red + format_template + reset,
}
def format(self, record):
@@ -27,33 +30,34 @@ class CustomFormatter(logging.Formatter):
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
def setup_logger(name: str) -> logging.Logger:
"""
Configures a custom logger
Args:
name: Logger name
Returns:
logging.Logger: Logger configurado
"""
logger = logging.getLogger(name)
# Remove existing handlers to avoid duplication
if logger.handlers:
logger.handlers.clear()
# Configure the logger level based on the environment variable or configuration
log_level = getattr(logging, os.getenv("LOG_LEVEL", settings.LOG_LEVEL).upper())
logger.setLevel(log_level)
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(CustomFormatter())
console_handler.setLevel(log_level)
logger.addHandler(console_handler)
# Prevent logs from being propagated to the root logger
logger.propagate = False
return logger
return logger

View File

@@ -11,41 +11,44 @@ from dataclasses import dataclass
logger = logging.getLogger(__name__)
# Fix bcrypt error with passlib
if not hasattr(bcrypt, '__about__'):
if not hasattr(bcrypt, "__about__"):
@dataclass
class BcryptAbout:
__version__: str = getattr(bcrypt, "__version__")
setattr(bcrypt, "__about__", BcryptAbout())
# Context for password hashing using bcrypt
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_password_hash(password: str) -> str:
"""Creates a password hash"""
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Verifies if the provided password matches the stored hash"""
return pwd_context.verify(plain_password, hashed_password)
def create_jwt_token(data: dict, expires_delta: timedelta = None) -> str:
"""Creates a JWT token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(
minutes=settings.JWT_EXPIRATION_TIME
)
expire = datetime.utcnow() + timedelta(minutes=settings.JWT_EXPIRATION_TIME)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(
to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM
)
return encoded_jwt
def generate_token(length: int = 32) -> str:
"""Generates a secure token for email verification or password reset"""
alphabet = string.ascii_letters + string.digits
token = ''.join(secrets.choice(alphabet) for _ in range(length))
return token
token = "".join(secrets.choice(alphabet) for _ in range(length))
return token