From 9c3467941b23f9ed68e417605740ff39f2e718ac Mon Sep 17 00:00:00 2001 From: akhdanre Date: Thu, 13 Mar 2025 14:15:09 +0700 Subject: [PATCH] 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