From 9c3467941b23f9ed68e417605740ff39f2e718ac Mon Sep 17 00:00:00 2001 From: akhdanre Date: Thu, 13 Mar 2025 14:15:09 +0700 Subject: [PATCH 1/8] feat: login with google done --- app/controllers/auth_controller.py | 6 +-- app/models/__init__.py | 5 +- app/models/entities/__init__.py | 3 ++ app/models/entities/user_entity.py | 17 ++++++ app/repositories/user_repository.py | 57 +++++++++++++++----- app/schemas/basic_response_schema.py | 2 +- app/schemas/google_login_schema.py | 2 +- app/services/auth_service.py | 77 +++++++++++++++++++--------- 8 files changed, 125 insertions(+), 44 deletions(-) create mode 100644 app/models/entities/__init__.py create mode 100644 app/models/entities/user_entity.py diff --git a/app/controllers/auth_controller.py b/app/controllers/auth_controller.py index 393aecf..8b78423 100644 --- a/app/controllers/auth_controller.py +++ b/app/controllers/auth_controller.py @@ -2,8 +2,8 @@ import logging import sys from flask import jsonify, request from pydantic import ValidationError -from app.schemas.basic_response_schema import ResponseSchema -from app.schemas.google_login_schema import GoogleLoginSchema +from schemas.basic_response_schema import ResponseSchema +from schemas.google_login_schema import GoogleLoginSchema from schemas import LoginSchema from services import UserService, AuthService @@ -32,7 +32,7 @@ class AuthController: # Validasi data dengan Pydantic validated_data = GoogleLoginSchema(**data) - id_token = validated_data.id_token + id_token = validated_data.token_id # Verifikasi ID Token ke layanan AuthService user_info = self.auth_service.verify_google_id_token(id_token) diff --git a/app/models/__init__.py b/app/models/__init__.py index fe3deb7..6677d42 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -1 +1,4 @@ -from .dto import ApiResponse +# app/models/__init__.py +from .entities import UserEntity + +__all__ = ["UserEntity", "UserDTO"] diff --git a/app/models/entities/__init__.py b/app/models/entities/__init__.py new file mode 100644 index 0000000..488041e --- /dev/null +++ b/app/models/entities/__init__.py @@ -0,0 +1,3 @@ +from .user_entity import UserEntity + +__all__ = ["UserEntity"] diff --git a/app/models/entities/user_entity.py b/app/models/entities/user_entity.py new file mode 100644 index 0000000..929e5f9 --- /dev/null +++ b/app/models/entities/user_entity.py @@ -0,0 +1,17 @@ +from typing import Optional +from pydantic import BaseModel, EmailStr +from datetime import date, datetime + + +class UserEntity(BaseModel): + id: str + google_id: Optional[str] = None + email: EmailStr + password: Optional[str] = None + name: str + birth_date: Optional[date] = None + pic_url: Optional[str] = None + phone: Optional[str] = None + locale: str = "en-US" + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None diff --git a/app/repositories/user_repository.py b/app/repositories/user_repository.py index 38207ae..906461b 100644 --- a/app/repositories/user_repository.py +++ b/app/repositories/user_repository.py @@ -1,4 +1,6 @@ -import sys +from typing import Optional +from bson import ObjectId +from models import UserEntity class UserRepository: @@ -6,18 +8,47 @@ class UserRepository: def __init__(self, db): self.collection = db.users - def get_all_users(self): - try: + def get_all_users(self) -> list[UserEntity]: + """Mengambil semua user dari database.""" + users = list(self.collection.find({}, {"_id": 0})) + return users if users else [] - users = list(self.collection.find({}, {"_id": 0})) + def get_user_by_email(self, email: str) -> Optional[UserEntity]: + """Mendapatkan user berdasarkan email.""" + user = self.collection.find_one({"email": email}, {"_id": 0}) + return user - return users if users else [] - except Exception as e: - return [] + def get_user_by_id(self, user_id: str) -> Optional[UserEntity]: + """Mendapatkan user berdasarkan ID.""" + object_id = ObjectId(user_id) + user = self.collection.find_one({"_id": object_id}) + return user - def get_user_by_email(self, email): - try: - user = self.collection.find_one({"email": email}, {"_id": 0}) - return user if user else None - except Exception as e: - return None + def get_by_google_id(self, google_id: str) -> Optional[UserEntity]: + user = self.collection.find_one({"google_id": google_id}) + return user + + def insert_user(self, user_data: UserEntity) -> str: + """Menambahkan pengguna baru ke dalam database dan mengembalikan ID pengguna.""" + result = self.collection.insert_one(user_data.model_dump()) + return str(result.inserted_id) + + def update_user(self, user_id: str, update_data: dict) -> bool: + """Mengupdate seluruh data user berdasarkan ID.""" + object_id = ObjectId(user_id) + result = self.collection.update_one({"_id": object_id}, {"$set": update_data}) + return result.modified_count > 0 + + def update_user_field(self, user_id: str, field: str, value) -> bool: + """Mengupdate satu field dari user berdasarkan ID.""" + object_id = ObjectId(user_id) + result = self.collection.update_one( + {"_id": object_id}, {"$set": {field: value}} + ) + return result.modified_count > 0 + + def delete_user(self, user_id: str) -> bool: + """Menghapus user berdasarkan ID.""" + object_id = ObjectId(user_id) + result = self.collection.delete_one({"_id": object_id}) + return result.deleted_count > 0 diff --git a/app/schemas/basic_response_schema.py b/app/schemas/basic_response_schema.py index c44d071..d5cf112 100644 --- a/app/schemas/basic_response_schema.py +++ b/app/schemas/basic_response_schema.py @@ -4,7 +4,7 @@ from pydantic import BaseModel T = TypeVar("T") -class MetaSchema: +class MetaSchema(BaseModel): total_page: int current_page: int total_data: int diff --git a/app/schemas/google_login_schema.py b/app/schemas/google_login_schema.py index ddd22da..f42a438 100644 --- a/app/schemas/google_login_schema.py +++ b/app/schemas/google_login_schema.py @@ -2,4 +2,4 @@ from pydantic import BaseModel class GoogleLoginSchema(BaseModel): - tokenId: str + token_id: str diff --git a/app/services/auth_service.py b/app/services/auth_service.py index 852765d..f335adf 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -1,10 +1,14 @@ +from datetime import datetime import sys +import traceback from schemas import LoginSchema from repositories import UserRepository -from models import ApiResponse + +# from models import ApiResponse from google.oauth2 import id_token from google.auth.transport import requests from configs import Config +from models import UserEntity class AuthService: @@ -13,28 +17,48 @@ class AuthService: 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 ) - print(f"output verify {payload}", file=sys.stderr) + if not payload: + print("Invalid token", file=sys.stderr) + return None - user_data = { - "_id": payload.get("sub"), - "email": payload.get("email"), - "name": payload.get("name"), - "picture": payload.get("picture"), - "given_name": payload.get("given_name"), - "family_name": payload.get("family_name"), - "locale": payload.get("locale"), - "email_verified": payload.get("email_verified", False), - "iat": payload.get("iat"), - "exp": payload.get("exp"), - } + google_id = payload.get("sub") + email = payload.get("email") + + existing_user = self.user_repository.get_by_google_id(google_id) + if existing_user: + if existing_user.email == email: + return existing_user + + 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, + ) + + # Simpan user ke database + user_id = self.user_repository.insert_user(user_data=new_user) + + # Ambil user yang baru dibuat berdasarkan ID + return self.user_repository.get_user_by_id(user_id=user_id) - return payload except Exception as e: - print(f"issue on the verify {e}", file=sys.stderr) + traceback.print_exc() + print(f"Error verifying Google ID token: {e}", file=sys.stderr) return None def login(self, data: LoginSchema): @@ -43,17 +67,20 @@ class AuthService: user_data = self.user_repository.get_user_by_email(data.email) if user_data == None: - return ApiResponse(success=False, message="User not found", data=None) + # return ApiResponse(success=False, message="User not found", data=None) + return None if user_data["password"] == data.password: del user_data["password"] - return ApiResponse( - success=True, message="Login success", data=user_data - ) - - return ApiResponse(success=False, message="Invalid password", data=None) + # return ApiResponse( + # success=True, message="Login success", data=user_data + # ) + return None + # return ApiResponse(success=False, message="Invalid password", data=None) + return None except Exception as e: print(f"the issue is {e}") - return ApiResponse( - success=False, message="Internal server error", data=None - ) + # return ApiResponse( + # success=False, message="Internal server error", data=None + # ) + return None From fab1cea6111e945ddef6e811cf863d15d4f5670c Mon Sep 17 00:00:00 2001 From: akhdanre Date: Wed, 19 Mar 2025 11:50:02 +0700 Subject: [PATCH 2/8] feat: error handling, logger configutration, and custom exception for auth --- app/configs/__init__.py | 1 + app/configs/logger_config.py | 35 ++++++++++++++++ app/controllers/auth_controller.py | 17 ++++---- app/core/__init__.py | 1 + app/core/exception/__init__.py | 1 + app/core/exception/auth_exception.py | 9 ++++ app/main.py | 8 ++-- app/mapper/__init__.py | 1 + app/mapper/user_mapper.py | 25 ++++++++++++ app/services/auth_service.py | 61 ++++++++-------------------- logs/app.log | 2 + logs/error.log | 0 logs/info.log | 3 ++ logs/warning.log | 0 14 files changed, 107 insertions(+), 57 deletions(-) create mode 100644 app/configs/logger_config.py create mode 100644 app/core/__init__.py create mode 100644 app/core/exception/__init__.py create mode 100644 app/core/exception/auth_exception.py create mode 100644 app/mapper/__init__.py create mode 100644 app/mapper/user_mapper.py create mode 100644 logs/app.log create mode 100644 logs/error.log create mode 100644 logs/info.log create mode 100644 logs/warning.log diff --git a/app/configs/__init__.py b/app/configs/__init__.py index cca5d9b..260ca6f 100644 --- a/app/configs/__init__.py +++ b/app/configs/__init__.py @@ -1 +1,2 @@ from .config import Config +from .logger_config import LoggerConfig diff --git a/app/configs/logger_config.py b/app/configs/logger_config.py new file mode 100644 index 0000000..60bbd33 --- /dev/null +++ b/app/configs/logger_config.py @@ -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) diff --git a/app/controllers/auth_controller.py b/app/controllers/auth_controller.py index 8b78423..4f7d927 100644 --- a/app/controllers/auth_controller.py +++ b/app/controllers/auth_controller.py @@ -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 ) diff --git a/app/core/__init__.py b/app/core/__init__.py new file mode 100644 index 0000000..00ddcd0 --- /dev/null +++ b/app/core/__init__.py @@ -0,0 +1 @@ +from .exception import AuthException diff --git a/app/core/exception/__init__.py b/app/core/exception/__init__.py new file mode 100644 index 0000000..d095db1 --- /dev/null +++ b/app/core/exception/__init__.py @@ -0,0 +1 @@ +from .auth_exception import AuthException diff --git a/app/core/exception/auth_exception.py b/app/core/exception/auth_exception.py new file mode 100644 index 0000000..3d4b28d --- /dev/null +++ b/app/core/exception/auth_exception.py @@ -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}" diff --git a/app/main.py b/app/main.py index 73252af..befa064 100644 --- a/app/main.py +++ b/app/main.py @@ -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 diff --git a/app/mapper/__init__.py b/app/mapper/__init__.py new file mode 100644 index 0000000..eeb7873 --- /dev/null +++ b/app/mapper/__init__.py @@ -0,0 +1 @@ +from .user_mapper import UserMapper diff --git a/app/mapper/user_mapper.py b/app/mapper/user_mapper.py new file mode 100644 index 0000000..999a4a2 --- /dev/null +++ b/app/mapper/user_mapper.py @@ -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, + ) diff --git a/app/services/auth_service.py b/app/services/auth_service.py index f335adf..f7fac39 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -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: diff --git a/logs/app.log b/logs/app.log new file mode 100644 index 0000000..5f7695d --- /dev/null +++ b/logs/app.log @@ -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. diff --git a/logs/error.log b/logs/error.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/info.log b/logs/info.log new file mode 100644 index 0000000..0b927e5 --- /dev/null +++ b/logs/info.log @@ -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. diff --git a/logs/warning.log b/logs/warning.log new file mode 100644 index 0000000..e69de29 From d63bf3e713211115ce1d070de028826701bd35d8 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Wed, 19 Mar 2025 13:51:13 +0700 Subject: [PATCH 3/8] fix: login with google is done --- .gitignore | 3 +++ app/configs/logger_config.py | 31 +++++++++++++++++++++++------ app/controllers/auth_controller.py | 7 ++++++- app/repositories/user_repository.py | 5 +++-- app/services/auth_service.py | 2 ++ logs/info.log | 3 --- 6 files changed, 39 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 4ca745a..3f3c67f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ app/**/*.pyo .env .venv + + +logs/ diff --git a/app/configs/logger_config.py b/app/configs/logger_config.py index 60bbd33..087a56f 100644 --- a/app/configs/logger_config.py +++ b/app/configs/logger_config.py @@ -16,20 +16,39 @@ class LoggerConfig: 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) + # Remove default handlers to prevent duplicate logging + for handler in app.logger.handlers[:]: + app.logger.removeHandler(handler) + # Create separate loggers + info_logger = LoggerConfig._setup_logger( + "info_logger", "info.log", logging.INFO, logging.WARNING + ) + error_logger = LoggerConfig._setup_logger( + "error_logger", "error.log", logging.ERROR, logging.CRITICAL + ) + warning_logger = LoggerConfig._setup_logger( + "warning_logger", "warning.log", logging.WARNING, logging.WARNING + ) + + # Attach handlers to Flask app logger + app.logger.addHandler(info_logger) + app.logger.addHandler(error_logger) + app.logger.addHandler(warning_logger) + + app.logger.setLevel(logging.DEBUG) # Set lowest level to capture all logs app.logger.info("Logger has been initialized for Flask application.") @staticmethod - def _setup_logger(logger, filename, level): + def _setup_logger(name, filename, level, max_level): """Helper method to configure loggers for specific levels.""" + logger = logging.getLogger(name) + logger.setLevel(level) 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) - + log_handler.addFilter(lambda record: level <= record.levelno <= max_level) logger.addHandler(log_handler) + return log_handler diff --git a/app/controllers/auth_controller.py b/app/controllers/auth_controller.py index 4f7d927..09a1012 100644 --- a/app/controllers/auth_controller.py +++ b/app/controllers/auth_controller.py @@ -4,6 +4,7 @@ from schemas.basic_response_schema import ResponseSchema from schemas.google_login_schema import GoogleLoginSchema from schemas import LoginSchema from services import UserService, AuthService +from core import AuthException class AuthController: @@ -38,7 +39,6 @@ class AuthController: ) return jsonify(response.model_dump()), 401 - # Jika berhasil, kembalikan data user tanpa meta response = ResponseSchema( message="Login successful", data=user_info, @@ -51,6 +51,11 @@ class AuthController: response = ResponseSchema(message="Invalid input", data=None, meta=None) return jsonify(response.model_dump()), 400 + except AuthException as e: + current_app.logger.error(f"Auth error: {e}") + response = ResponseSchema(message=e, data=None, meta=None) + return jsonify(response.model_dump()), 400 + except Exception as e: current_app.logger.error( f"Error during Google login: {str(e)}", exc_info=True diff --git a/app/repositories/user_repository.py b/app/repositories/user_repository.py index 906461b..9e0048f 100644 --- a/app/repositories/user_repository.py +++ b/app/repositories/user_repository.py @@ -25,8 +25,9 @@ class UserRepository: return user def get_by_google_id(self, google_id: str) -> Optional[UserEntity]: - user = self.collection.find_one({"google_id": google_id}) - return user + user_data = self.collection.find_one({"google_id": google_id}) + + return UserEntity(**user_data) if user_data else None def insert_user(self, user_data: UserEntity) -> str: """Menambahkan pengguna baru ke dalam database dan mengembalikan ID pengguna.""" diff --git a/app/services/auth_service.py b/app/services/auth_service.py index f7fac39..699e11f 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -5,6 +5,7 @@ from google.oauth2 import id_token from google.auth.transport import requests from configs import Config from core import AuthException +from flask import current_app class AuthService: @@ -26,6 +27,7 @@ class AuthService: existing_user = self.user_repository.get_by_google_id(google_id) if existing_user: + current_app.logger.info(f"User {existing_user.email} already exists ") if existing_user.email == email: return existing_user return AuthException("Email not match") diff --git a/logs/info.log b/logs/info.log index 0b927e5..e69de29 100644 --- a/logs/info.log +++ b/logs/info.log @@ -1,3 +0,0 @@ -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. From 7f5479188eab81c04b8bbc37f08118bbceab3003 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Wed, 19 Mar 2025 13:53:11 +0700 Subject: [PATCH 4/8] feat: adding converter from dict to User entity for user repository --- app/repositories/user_repository.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/repositories/user_repository.py b/app/repositories/user_repository.py index 9e0048f..2d89941 100644 --- a/app/repositories/user_repository.py +++ b/app/repositories/user_repository.py @@ -11,18 +11,18 @@ class UserRepository: def get_all_users(self) -> list[UserEntity]: """Mengambil semua user dari database.""" users = list(self.collection.find({}, {"_id": 0})) - return users if users else [] + return [UserEntity(**user) for user in users] def get_user_by_email(self, email: str) -> Optional[UserEntity]: """Mendapatkan user berdasarkan email.""" user = self.collection.find_one({"email": email}, {"_id": 0}) - return user + return UserEntity(**user) if user else None def get_user_by_id(self, user_id: str) -> Optional[UserEntity]: """Mendapatkan user berdasarkan ID.""" object_id = ObjectId(user_id) user = self.collection.find_one({"_id": object_id}) - return user + return UserEntity(**user) if user else None def get_by_google_id(self, google_id: str) -> Optional[UserEntity]: user_data = self.collection.find_one({"google_id": google_id}) From bc5094ed7dddb4a3f47fae1d5ec1f250a6ba4290 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Wed, 19 Mar 2025 20:03:20 +0700 Subject: [PATCH 5/8] fix: login with id from mongo --- app/controllers/auth_controller.py | 26 ++++++++++++++++++++------ app/mapper/user_mapper.py | 1 - app/models/entities/__init__.py | 6 +++++- app/models/entities/base.py | 19 +++++++++++++++++++ app/models/entities/user_entity.py | 3 ++- app/services/auth_service.py | 27 +++++++-------------------- logs/app.log | 2 -- logs/error.log | 0 logs/info.log | 0 logs/warning.log | 0 10 files changed, 53 insertions(+), 31 deletions(-) create mode 100644 app/models/entities/base.py delete mode 100644 logs/app.log delete mode 100644 logs/error.log delete mode 100644 logs/info.log delete mode 100644 logs/warning.log diff --git a/app/controllers/auth_controller.py b/app/controllers/auth_controller.py index 09a1012..448cedf 100644 --- a/app/controllers/auth_controller.py +++ b/app/controllers/auth_controller.py @@ -13,13 +13,27 @@ class AuthController: self.auth_service = authService def login(self): - data = request.get_json() - dataSchema = LoginSchema(**data) - response = self.auth_service.login(dataSchema) + try: + data = request.get_json() + dataSchema = LoginSchema(**data) + response = self.auth_service.login(dataSchema) - if response.success: - return jsonify(response.to_dict()), 200 - return jsonify(response.to_dict()), 400 + if response.success: + return jsonify({}), 200 + return jsonify({}), 400 + except ValidationError as 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: + current_app.logger.error( + f"Error during Google login: {str(e)}", exc_info=True + ) + response = ResponseSchema( + message="Internal server error", data=None, meta=None + ) + return jsonify(response.model_dump()), 500 def google_login(self): """Handles Google Login via ID Token verification""" diff --git a/app/mapper/user_mapper.py b/app/mapper/user_mapper.py index 999a4a2..6b89c90 100644 --- a/app/mapper/user_mapper.py +++ b/app/mapper/user_mapper.py @@ -9,7 +9,6 @@ class UserMapper: 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"), diff --git a/app/models/entities/__init__.py b/app/models/entities/__init__.py index 488041e..b5893dc 100644 --- a/app/models/entities/__init__.py +++ b/app/models/entities/__init__.py @@ -1,3 +1,7 @@ from .user_entity import UserEntity +from .base import PyObjectId -__all__ = ["UserEntity"] +__all__ = [ + "UserEntity", + "PyObjectId", +] diff --git a/app/models/entities/base.py b/app/models/entities/base.py new file mode 100644 index 0000000..b602169 --- /dev/null +++ b/app/models/entities/base.py @@ -0,0 +1,19 @@ +from bson import ObjectId + + +class PyObjectId(ObjectId): + """Custom ObjectId type for Pydantic to handle MongoDB _id""" + + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, v): + if not ObjectId.is_valid(v): + raise ValueError("Invalid ObjectId") + return ObjectId(v) + + @classmethod + def __modify_schema__(cls, field_schema): + field_schema.update(type="string") diff --git a/app/models/entities/user_entity.py b/app/models/entities/user_entity.py index 929e5f9..9d74109 100644 --- a/app/models/entities/user_entity.py +++ b/app/models/entities/user_entity.py @@ -1,10 +1,11 @@ from typing import Optional from pydantic import BaseModel, EmailStr from datetime import date, datetime +from .base import PyObjectId class UserEntity(BaseModel): - id: str + _id: Optional[PyObjectId] = None google_id: Optional[str] = None email: EmailStr password: Optional[str] = None diff --git a/app/services/auth_service.py b/app/services/auth_service.py index 699e11f..1807bcf 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -27,7 +27,6 @@ class AuthService: existing_user = self.user_repository.get_by_google_id(google_id) if existing_user: - current_app.logger.info(f"User {existing_user.email} already exists ") if existing_user.email == email: return existing_user return AuthException("Email not match") @@ -39,25 +38,13 @@ class AuthService: return self.user_repository.get_user_by_id(user_id=user_id) def login(self, data: LoginSchema): - try: - user_data = self.user_repository.get_user_by_email(data.email) + user_data = self.user_repository.get_user_by_email(data.email) - if user_data == None: - # return ApiResponse(success=False, message="User not found", data=None) - return None - - if user_data["password"] == data.password: - del user_data["password"] - # return ApiResponse( - # success=True, message="Login success", data=user_data - # ) - return None - # return ApiResponse(success=False, message="Invalid password", data=None) - return None - except Exception as e: - print(f"the issue is {e}") - # return ApiResponse( - # success=False, message="Internal server error", data=None - # ) + if user_data == None: return None + + if user_data.password == data.password: + del user_data.password + return user_data + return None diff --git a/logs/app.log b/logs/app.log deleted file mode 100644 index 5f7695d..0000000 --- a/logs/app.log +++ /dev/null @@ -1,2 +0,0 @@ -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. diff --git a/logs/error.log b/logs/error.log deleted file mode 100644 index e69de29..0000000 diff --git a/logs/info.log b/logs/info.log deleted file mode 100644 index e69de29..0000000 diff --git a/logs/warning.log b/logs/warning.log deleted file mode 100644 index e69de29..0000000 From c06b7ab899b3430314b666cbadac86b63a95433c Mon Sep 17 00:00:00 2001 From: akhdanre Date: Wed, 19 Mar 2025 22:35:02 +0700 Subject: [PATCH 6/8] feat: register done --- app/blueprints/__init__.py | 1 + app/blueprints/auth.py | 7 ----- app/blueprints/user.py | 14 ++++++--- app/controllers/auth_controller.py | 16 ++++------ app/controllers/user_controller.py | 37 +++++++++++++++++++++--- app/core/__init__.py | 1 - app/core/exception/__init__.py | 1 - app/core/exception/auth_exception.py | 9 ------ app/di_container.py | 2 ++ app/exception/__init__.py | 2 ++ app/exception/already_exist_exception.py | 6 ++++ app/exception/auth_exception.py | 8 +++++ app/exception/base_exception.py | 10 +++++++ app/main.py | 4 ++- app/mapper/user_mapper.py | 17 +++++++++++ app/models/entities/user_entity.py | 4 +-- app/schemas/__init__.py | 1 + app/schemas/requests/__init__.py | 1 + app/schemas/requests/register_schema.py | 10 +++++++ app/services/auth_service.py | 8 ++--- app/services/user_service.py | 12 ++++++++ 21 files changed, 126 insertions(+), 45 deletions(-) delete mode 100644 app/core/__init__.py delete mode 100644 app/core/exception/__init__.py delete mode 100644 app/core/exception/auth_exception.py create mode 100644 app/exception/__init__.py create mode 100644 app/exception/already_exist_exception.py create mode 100644 app/exception/auth_exception.py create mode 100644 app/exception/base_exception.py create mode 100644 app/schemas/requests/__init__.py create mode 100644 app/schemas/requests/register_schema.py diff --git a/app/blueprints/__init__.py b/app/blueprints/__init__.py index ffac468..9a7a4d3 100644 --- a/app/blueprints/__init__.py +++ b/app/blueprints/__init__.py @@ -1,5 +1,6 @@ from .default import default_blueprint from .auth import auth_blueprint +from .user import user_blueprint # from .user import user_blueprint diff --git a/app/blueprints/auth.py b/app/blueprints/auth.py index 5834846..ce87c62 100644 --- a/app/blueprints/auth.py +++ b/app/blueprints/auth.py @@ -1,4 +1,3 @@ -import sys from flask import Blueprint from controllers import AuthController from di_container import Container @@ -8,12 +7,6 @@ from dependency_injector.wiring import inject, Provide auth_blueprint = Blueprint("auth", __name__) -@auth_blueprint.route("/register", methods=["POST"]) -@inject -def register(auth_controller: AuthController = Provide[Container.auth_controller]): - return auth_controller.register() - - @auth_blueprint.route("/login", methods=["POST"]) @inject def login(auth_controller: AuthController = Provide[Container.auth_controller]): diff --git a/app/blueprints/user.py b/app/blueprints/user.py index d5b7d5e..182a318 100644 --- a/app/blueprints/user.py +++ b/app/blueprints/user.py @@ -1,12 +1,18 @@ from flask import Blueprint from controllers import UserController -from di_container import container +from di_container import Container +from dependency_injector.wiring import inject, Provide user_blueprint = Blueprint("user", __name__) -user_controller = UserController(container.user_service) - @user_blueprint.route("/users", methods=["GET"]) -def get_users(): +@inject +def get_users(user_controller: UserController = Provide[Container.user_controller]): return user_controller.get_users() + + +@user_blueprint.route("/register", methods=["POST"]) +@inject +def register(user_controller: UserController = Provide[Container.user_controller]): + return user_controller.register() diff --git a/app/controllers/auth_controller.py b/app/controllers/auth_controller.py index 448cedf..f467814 100644 --- a/app/controllers/auth_controller.py +++ b/app/controllers/auth_controller.py @@ -4,7 +4,7 @@ from schemas.basic_response_schema import ResponseSchema from schemas.google_login_schema import GoogleLoginSchema from schemas import LoginSchema from services import UserService, AuthService -from core import AuthException +from exception import AuthException class AuthController: @@ -17,10 +17,10 @@ class AuthController: data = request.get_json() dataSchema = LoginSchema(**data) response = self.auth_service.login(dataSchema) - - if response.success: - return jsonify({}), 200 - return jsonify({}), 400 + return ( + jsonify(ResponseSchema(message="Register success", data=response)), + 200, + ) except ValidationError as e: current_app.logger.error(f"Validation error: {e}") response = ResponseSchema(message="Invalid input", data=None, meta=None) @@ -79,11 +79,5 @@ class AuthController: ) return jsonify(response.model_dump()), 500 - def register(self): - return jsonify({"message": "register"}), 200 - def logout(self): return jsonify({"message": "logout"}), 200 - - def test(self): - return "test" diff --git a/app/controllers/user_controller.py b/app/controllers/user_controller.py index d642a3b..b05cd2c 100644 --- a/app/controllers/user_controller.py +++ b/app/controllers/user_controller.py @@ -1,12 +1,41 @@ # /controllers/user_controller.py -from flask import jsonify +from flask import jsonify, request, current_app from services import UserService +from schemas import RegisterSchema +from pydantic import ValidationError +from schemas import ResponseSchema +from exception import AlreadyExistException class UserController: def __init__(self, userService: UserService): self.user_service = userService - def get_users(self): - users = self.user_service.get_all_users() - return jsonify(users) + def register(self): + try: + request_data = request.get_json() + register_data = RegisterSchema(**request_data) + self.user_service.register_user(register_data) + return jsonify(ResponseSchema(message="Register Success").model_dump()), 200 + + except ValidationError as 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 AlreadyExistException as e: + return ( + jsonify( + ResponseSchema(message=str(e), data=None, meta=None).model_dump() + ), + 409, + ) + + except Exception as e: + current_app.logger.error( + f"Error during Google login: {str(e)}", exc_info=True + ) + response = ResponseSchema( + message="Internal server error", data=None, meta=None + ) + return jsonify(response.model_dump()), 500 diff --git a/app/core/__init__.py b/app/core/__init__.py deleted file mode 100644 index 00ddcd0..0000000 --- a/app/core/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .exception import AuthException diff --git a/app/core/exception/__init__.py b/app/core/exception/__init__.py deleted file mode 100644 index d095db1..0000000 --- a/app/core/exception/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .auth_exception import AuthException diff --git a/app/core/exception/auth_exception.py b/app/core/exception/auth_exception.py deleted file mode 100644 index 3d4b28d..0000000 --- a/app/core/exception/auth_exception.py +++ /dev/null @@ -1,9 +0,0 @@ -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}" diff --git a/app/di_container.py b/app/di_container.py index fb7cef5..4a3f7ac 100644 --- a/app/di_container.py +++ b/app/di_container.py @@ -1,4 +1,5 @@ from dependency_injector import containers, providers +from controllers import UserController from repositories.user_repository import UserRepository from services import UserService, AuthService from controllers import AuthController @@ -19,3 +20,4 @@ class Container(containers.DeclarativeContainer): # controllers auth_controller = providers.Factory(AuthController, user_service, auth_service) + user_controller = providers.Factory(UserController, user_service) diff --git a/app/exception/__init__.py b/app/exception/__init__.py new file mode 100644 index 0000000..3b9d50d --- /dev/null +++ b/app/exception/__init__.py @@ -0,0 +1,2 @@ +from .auth_exception import AuthException +from .already_exist_exception import AlreadyExistException diff --git a/app/exception/already_exist_exception.py b/app/exception/already_exist_exception.py new file mode 100644 index 0000000..f3ac645 --- /dev/null +++ b/app/exception/already_exist_exception.py @@ -0,0 +1,6 @@ +class AlreadyExistException(Exception): + def __init__(self, entity: str, message: str = None): + if message is None: + message = f"{entity} already exists" + self.message = message + super().__init__(self.message) diff --git a/app/exception/auth_exception.py b/app/exception/auth_exception.py new file mode 100644 index 0000000..12378f5 --- /dev/null +++ b/app/exception/auth_exception.py @@ -0,0 +1,8 @@ +from .base_exception import BaseExceptionTemplate + + +class AuthException(BaseExceptionTemplate): + """Exception for authentication-related errors""" + + def __init__(self, message: str = "Authentication failed"): + super().__init__(message, status_code=401) diff --git a/app/exception/base_exception.py b/app/exception/base_exception.py new file mode 100644 index 0000000..747b739 --- /dev/null +++ b/app/exception/base_exception.py @@ -0,0 +1,10 @@ +class BaseExceptionTemplate(Exception): + """Base exception template for custom exceptions""" + + def __init__(self, message: str, status_code: int = 400): + self.message = message + self.status_code = status_code + super().__init__(self.message) + + def __str__(self): + return f"{self.__class__.__name__}: {self.message}" diff --git a/app/main.py b/app/main.py index befa064..095c0b9 100644 --- a/app/main.py +++ b/app/main.py @@ -2,7 +2,7 @@ from blueprints import default_blueprint from di_container import Container from configs import Config, LoggerConfig from flask import Flask -from blueprints import auth_blueprint +from blueprints import auth_blueprint, user_blueprint from database import init_db @@ -19,10 +19,12 @@ def createApp() -> Flask: container.mongo.override(mongo) container.wire(modules=["blueprints.auth"]) + container.wire(modules=["blueprints.user"]) # Register Blueprints app.register_blueprint(default_blueprint) app.register_blueprint(auth_blueprint, url_prefix="/api") + app.register_blueprint(user_blueprint, url_prefix="/api") # Initialize Logging LoggerConfig.init_logger(app) diff --git a/app/mapper/user_mapper.py b/app/mapper/user_mapper.py index 6b89c90..d7f16be 100644 --- a/app/mapper/user_mapper.py +++ b/app/mapper/user_mapper.py @@ -1,6 +1,7 @@ from datetime import datetime from typing import Dict, Optional from models import UserEntity +from schemas import RegisterSchema class UserMapper: @@ -22,3 +23,19 @@ class UserMapper: updated_at=datetime.now(), verification_token=None, ) + + @staticmethod + def from_register(data: RegisterSchema) -> UserEntity: + return UserEntity( + email=data.email, + password=data.password, + name=data.name, + birth_date=datetime.strptime(data.birth_date, "%d-%m-%Y").date(), + phone=data.phone, + role="user", + is_active=False, + address=None, + created_at=datetime.now(), + updated_at=datetime.now(), + verification_token=None, + ) diff --git a/app/models/entities/user_entity.py b/app/models/entities/user_entity.py index 9d74109..926ec8d 100644 --- a/app/models/entities/user_entity.py +++ b/app/models/entities/user_entity.py @@ -1,6 +1,6 @@ from typing import Optional from pydantic import BaseModel, EmailStr -from datetime import date, datetime +from datetime import datetime from .base import PyObjectId @@ -10,7 +10,7 @@ class UserEntity(BaseModel): email: EmailStr password: Optional[str] = None name: str - birth_date: Optional[date] = None + birth_date: Optional[datetime] = None pic_url: Optional[str] = None phone: Optional[str] = None locale: str = "en-US" diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py index 77d9edf..215170a 100644 --- a/app/schemas/__init__.py +++ b/app/schemas/__init__.py @@ -1,2 +1,3 @@ from .login_schema import LoginSchema from .basic_response_schema import ResponseSchema, MetaSchema +from .requests import RegisterSchema diff --git a/app/schemas/requests/__init__.py b/app/schemas/requests/__init__.py new file mode 100644 index 0000000..a6389c0 --- /dev/null +++ b/app/schemas/requests/__init__.py @@ -0,0 +1 @@ +from .register_schema import RegisterSchema diff --git a/app/schemas/requests/register_schema.py b/app/schemas/requests/register_schema.py new file mode 100644 index 0000000..57d6006 --- /dev/null +++ b/app/schemas/requests/register_schema.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel +from typing import Optional + + +class RegisterSchema(BaseModel): + email: str + password: str + name: str + birth_date: str + phone: Optional[str] = None diff --git a/app/services/auth_service.py b/app/services/auth_service.py index 1807bcf..22134e9 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -4,7 +4,7 @@ from mapper import UserMapper from google.oauth2 import id_token from google.auth.transport import requests from configs import Config -from core import AuthException +from exception import AuthException from flask import current_app @@ -13,14 +13,12 @@ class AuthService: self.user_repository = userRepository def verify_google_id_token(self, id_token_str): - - # Verifikasi token Google payload = id_token.verify_oauth2_token( id_token_str, requests.Request(), Config.GOOGLE_CLIENT_ID ) if not payload: - return AuthException("Invalid Google ID Token") + raise AuthException("Invalid Google ID Token") google_id = payload.get("sub") email = payload.get("email") @@ -29,7 +27,7 @@ class AuthService: if existing_user: if existing_user.email == email: return existing_user - return AuthException("Email not match") + raise AuthException("Email not match") new_user = UserMapper.from_google_payload(google_id, email, payload) diff --git a/app/services/user_service.py b/app/services/user_service.py index 8da1b75..bc3f509 100644 --- a/app/services/user_service.py +++ b/app/services/user_service.py @@ -1,4 +1,8 @@ +from flask import current_app from repositories import UserRepository +from schemas import RegisterSchema +from mapper import UserMapper +from exception import AlreadyExistException class UserService: @@ -7,3 +11,11 @@ class UserService: def get_all_users(self): return self.user_repository.get_all_users() + + def register_user(self, user_data: RegisterSchema): + existData = self.user_repository.get_user_by_email(user_data.email) + if existData: + raise AlreadyExistException(entity="Email") + + data = UserMapper.from_register(user_data) + return self.user_repository.insert_user(data) From b3907455e12d698ad0a5cf88456e7d1ba198cee6 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Thu, 20 Mar 2025 13:11:44 +0700 Subject: [PATCH 7/8] fix: logger --- app/controllers/auth_controller.py | 8 ++++++-- app/database/db.py | 8 +++----- app/main.py | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/controllers/auth_controller.py b/app/controllers/auth_controller.py index f467814..ac6e6cd 100644 --- a/app/controllers/auth_controller.py +++ b/app/controllers/auth_controller.py @@ -18,7 +18,11 @@ class AuthController: dataSchema = LoginSchema(**data) response = self.auth_service.login(dataSchema) return ( - jsonify(ResponseSchema(message="Register success", data=response)), + jsonify( + ResponseSchema( + message="Register success", data=response + ).model_dump() + ), 200, ) except ValidationError as e: @@ -30,7 +34,7 @@ class AuthController: current_app.logger.error( f"Error during Google login: {str(e)}", exc_info=True ) - response = ResponseSchema( + response = ResponseSchema( message="Internal server error", data=None, meta=None ) return jsonify(response.model_dump()), 500 diff --git a/app/database/db.py b/app/database/db.py index 90f04e0..fe50adb 100644 --- a/app/database/db.py +++ b/app/database/db.py @@ -1,6 +1,5 @@ from flask_pymongo import PyMongo -from flask import Flask -from configs import Config +from flask import Flask, current_app def init_db(app: Flask) -> PyMongo: @@ -8,10 +7,9 @@ def init_db(app: Flask) -> PyMongo: mongo = PyMongo(app) mongo.cx.server_info() - print("✅ MongoDB connection successful!") - + app.logger.info("MongoDB connection established") return mongo except Exception as e: - print(f"❌ MongoDB connection failed: {e}") + app.logger.error(f"MongoDB connection failed: {e}") return None # Handle failure gracefully diff --git a/app/main.py b/app/main.py index 095c0b9..01a6d56 100644 --- a/app/main.py +++ b/app/main.py @@ -9,6 +9,7 @@ from database import init_db def createApp() -> Flask: app = Flask(__name__) app.config.from_object(Config) + LoggerConfig.init_logger(app) container = Container() @@ -27,7 +28,6 @@ def createApp() -> Flask: app.register_blueprint(user_blueprint, url_prefix="/api") # Initialize Logging - LoggerConfig.init_logger(app) return app From 5798a72e4ebf604e8742fe84a681ea34f67e14b0 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Thu, 20 Mar 2025 18:41:05 +0700 Subject: [PATCH 8/8] fix: minor --- app/main.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/main.py b/app/main.py index 01a6d56..a1bf450 100644 --- a/app/main.py +++ b/app/main.py @@ -27,8 +27,6 @@ def createApp() -> Flask: app.register_blueprint(auth_blueprint, url_prefix="/api") app.register_blueprint(user_blueprint, url_prefix="/api") - # Initialize Logging - return app