feat: error handling, logger configutration, and custom exception for auth

This commit is contained in:
akhdanre 2025-03-19 11:50:02 +07:00
parent 9c3467941b
commit fab1cea611
14 changed files with 107 additions and 57 deletions

View File

@ -1 +1,2 @@
from .config import Config
from .logger_config import LoggerConfig

View File

@ -0,0 +1,35 @@
import logging
import os
from logging.handlers import RotatingFileHandler
class LoggerConfig:
"""A class to configure logging for the Flask application."""
LOG_DIR = "logs" # Define the log directory
@staticmethod
def init_logger(app):
"""Initializes separate log files for INFO, ERROR, and WARNING levels."""
# Ensure the logs directory exists
if not os.path.exists(LoggerConfig.LOG_DIR):
os.makedirs(LoggerConfig.LOG_DIR)
# Separate loggers for different levels
LoggerConfig._setup_logger(app.logger, "info.log", logging.INFO)
LoggerConfig._setup_logger(app.logger, "error.log", logging.ERROR)
LoggerConfig._setup_logger(app.logger, "warning.log", logging.WARNING)
app.logger.info("Logger has been initialized for Flask application.")
@staticmethod
def _setup_logger(logger, filename, level):
"""Helper method to configure loggers for specific levels."""
log_file_path = os.path.join(LoggerConfig.LOG_DIR, filename)
log_handler = RotatingFileHandler(log_file_path, maxBytes=100000, backupCount=3)
log_handler.setLevel(level)
log_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
log_handler.setFormatter(log_formatter)
logger.addHandler(log_handler)

View File

@ -1,15 +1,10 @@
import logging
import sys
from flask import jsonify, request
from flask import jsonify, request, current_app
from pydantic import ValidationError
from schemas.basic_response_schema import ResponseSchema
from schemas.google_login_schema import GoogleLoginSchema
from schemas import LoginSchema
from services import UserService, AuthService
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class AuthController:
def __init__(self, userService: UserService, authService: AuthService):
@ -37,7 +32,7 @@ class AuthController:
# Verifikasi ID Token ke layanan AuthService
user_info = self.auth_service.verify_google_id_token(id_token)
if not user_info:
logger.error("Invalid Google ID Token")
current_app.logger.error("Invalid Google ID Token")
response = ResponseSchema(
message="Invalid Google ID Token", data=None, meta=None
)
@ -47,17 +42,19 @@ class AuthController:
response = ResponseSchema(
message="Login successful",
data=user_info,
meta=None, # Karena ini single data, tidak ada meta
meta=None,
)
return jsonify(response.model_dump()), 200
except ValidationError as e:
logger.error(f"Validation error: {e}")
current_app.logger.error(f"Validation error: {e}")
response = ResponseSchema(message="Invalid input", data=None, meta=None)
return jsonify(response.model_dump()), 400
except Exception as e:
logger.error(f"Error during Google login: {str(e)}", exc_info=True)
current_app.logger.error(
f"Error during Google login: {str(e)}", exc_info=True
)
response = ResponseSchema(
message="Internal server error", data=None, meta=None
)

1
app/core/__init__.py Normal file
View File

@ -0,0 +1 @@
from .exception import AuthException

View File

@ -0,0 +1 @@
from .auth_exception import AuthException

View File

@ -0,0 +1,9 @@
class AuthException(Exception):
"""Custom exception for authentication-related errors"""
def __init__(self, message):
super().__init__(message)
self.message = message
def __str__(self):
return f"AuthException: {self.message}"

View File

@ -1,9 +1,6 @@
import sys
from dotenv import load_dotenv
from blueprints import default_blueprint
from di_container import Container
from configs import Config
from configs import Config, LoggerConfig
from flask import Flask
from blueprints import auth_blueprint
from database import init_db
@ -27,6 +24,9 @@ def createApp() -> Flask:
app.register_blueprint(default_blueprint)
app.register_blueprint(auth_blueprint, url_prefix="/api")
# Initialize Logging
LoggerConfig.init_logger(app)
return app

1
app/mapper/__init__.py Normal file
View File

@ -0,0 +1 @@
from .user_mapper import UserMapper

25
app/mapper/user_mapper.py Normal file
View File

@ -0,0 +1,25 @@
from datetime import datetime
from typing import Dict, Optional
from models import UserEntity
class UserMapper:
@staticmethod
def from_google_payload(
google_id: str, email: str, payload: Dict[str, Optional[str]]
) -> UserEntity:
return UserEntity(
id=str(google_id),
google_id=google_id,
email=email,
name=payload.get("name"),
pic_url=payload.get("picture"),
birth_date=None,
phone=None,
role="user",
is_active=True,
address=None,
created_at=datetime.now(),
updated_at=datetime.now(),
verification_token=None,
)

View File

@ -1,14 +1,10 @@
from datetime import datetime
import sys
import traceback
from schemas import LoginSchema
from repositories import UserRepository
# from models import ApiResponse
from mapper import UserMapper
from google.oauth2 import id_token
from google.auth.transport import requests
from configs import Config
from models import UserEntity
from core import AuthException
class AuthService:
@ -16,50 +12,29 @@ class AuthService:
self.user_repository = userRepository
def verify_google_id_token(self, id_token_str):
try:
# Verifikasi token Google
payload = id_token.verify_oauth2_token(
id_token_str, requests.Request(), Config.GOOGLE_CLIENT_ID
)
if not payload:
print("Invalid token", file=sys.stderr)
return None
# Verifikasi token Google
payload = id_token.verify_oauth2_token(
id_token_str, requests.Request(), Config.GOOGLE_CLIENT_ID
)
google_id = payload.get("sub")
email = payload.get("email")
if not payload:
return AuthException("Invalid Google ID Token")
existing_user = self.user_repository.get_by_google_id(google_id)
if existing_user:
if existing_user.email == email:
return existing_user
google_id = payload.get("sub")
email = payload.get("email")
new_user = UserEntity(
id=str(google_id),
google_id=google_id,
email=email,
name=payload.get("name"),
pic_url=payload.get("picture"),
birth_date=None,
phone=None,
role="user",
is_active=True,
address=None,
created_at=datetime.now(),
updated_at=datetime.now(),
verification_token=None,
)
existing_user = self.user_repository.get_by_google_id(google_id)
if existing_user:
if existing_user.email == email:
return existing_user
return AuthException("Email not match")
# Simpan user ke database
user_id = self.user_repository.insert_user(user_data=new_user)
new_user = UserMapper.from_google_payload(google_id, email, payload)
# Ambil user yang baru dibuat berdasarkan ID
return self.user_repository.get_user_by_id(user_id=user_id)
user_id = self.user_repository.insert_user(user_data=new_user)
except Exception as e:
traceback.print_exc()
print(f"Error verifying Google ID token: {e}", file=sys.stderr)
return None
return self.user_repository.get_user_by_id(user_id=user_id)
def login(self, data: LoginSchema):
try:

2
logs/app.log Normal file
View File

@ -0,0 +1,2 @@
2025-03-19 11:45:54,493 - INFO - Logger has been initialized for Flask application.
2025-03-19 11:46:06,381 - INFO - Logger has been initialized for Flask application.

0
logs/error.log Normal file
View File

3
logs/info.log Normal file
View File

@ -0,0 +1,3 @@
2025-03-19 11:48:56,950 - INFO - Logger has been initialized for Flask application.
2025-03-19 11:49:01,795 - INFO - Logger has been initialized for Flask application.
2025-03-19 11:49:06,664 - INFO - Logger has been initialized for Flask application.

0
logs/warning.log Normal file
View File