feat: error handling, logger configutration, and custom exception for auth
This commit is contained in:
parent
9c3467941b
commit
fab1cea611
|
@ -1 +1,2 @@
|
|||
from .config import Config
|
||||
from .logger_config import LoggerConfig
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from .exception import AuthException
|
|
@ -0,0 +1 @@
|
|||
from .auth_exception import AuthException
|
|
@ -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}"
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from .user_mapper import UserMapper
|
|
@ -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,
|
||||
)
|
|
@ -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:
|
||||
|
|
|
@ -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,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.
|
Loading…
Reference in New Issue