feat: login with google done

This commit is contained in:
akhdanre 2025-03-13 14:15:09 +07:00
parent a5f9953045
commit 9c3467941b
8 changed files with 125 additions and 44 deletions

View File

@ -2,8 +2,8 @@ import logging
import sys import sys
from flask import jsonify, request from flask import jsonify, request
from pydantic import ValidationError from pydantic import ValidationError
from app.schemas.basic_response_schema import ResponseSchema from schemas.basic_response_schema import ResponseSchema
from app.schemas.google_login_schema import GoogleLoginSchema from schemas.google_login_schema import GoogleLoginSchema
from schemas import LoginSchema from schemas import LoginSchema
from services import UserService, AuthService from services import UserService, AuthService
@ -32,7 +32,7 @@ class AuthController:
# Validasi data dengan Pydantic # Validasi data dengan Pydantic
validated_data = GoogleLoginSchema(**data) validated_data = GoogleLoginSchema(**data)
id_token = validated_data.id_token id_token = validated_data.token_id
# Verifikasi ID Token ke layanan AuthService # Verifikasi ID Token ke layanan AuthService
user_info = self.auth_service.verify_google_id_token(id_token) user_info = self.auth_service.verify_google_id_token(id_token)

View File

@ -1 +1,4 @@
from .dto import ApiResponse # app/models/__init__.py
from .entities import UserEntity
__all__ = ["UserEntity", "UserDTO"]

View File

@ -0,0 +1,3 @@
from .user_entity import UserEntity
__all__ = ["UserEntity"]

View File

@ -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

View File

@ -1,4 +1,6 @@
import sys from typing import Optional
from bson import ObjectId
from models import UserEntity
class UserRepository: class UserRepository:
@ -6,18 +8,47 @@ class UserRepository:
def __init__(self, db): def __init__(self, db):
self.collection = db.users self.collection = db.users
def get_all_users(self): def get_all_users(self) -> list[UserEntity]:
try: """Mengambil semua user dari database."""
users = list(self.collection.find({}, {"_id": 0})) users = list(self.collection.find({}, {"_id": 0}))
return users if users else [] return users if users else []
except Exception as e:
return []
def get_user_by_email(self, email): def get_user_by_email(self, email: str) -> Optional[UserEntity]:
try: """Mendapatkan user berdasarkan email."""
user = self.collection.find_one({"email": email}, {"_id": 0}) user = self.collection.find_one({"email": email}, {"_id": 0})
return user if user else None return user
except Exception as e:
return 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
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

View File

@ -4,7 +4,7 @@ from pydantic import BaseModel
T = TypeVar("T") T = TypeVar("T")
class MetaSchema: class MetaSchema(BaseModel):
total_page: int total_page: int
current_page: int current_page: int
total_data: int total_data: int

View File

@ -2,4 +2,4 @@ from pydantic import BaseModel
class GoogleLoginSchema(BaseModel): class GoogleLoginSchema(BaseModel):
tokenId: str token_id: str

View File

@ -1,10 +1,14 @@
from datetime import datetime
import sys import sys
import traceback
from schemas import LoginSchema from schemas import LoginSchema
from repositories import UserRepository from repositories import UserRepository
from models import ApiResponse
# from models import ApiResponse
from google.oauth2 import id_token from google.oauth2 import id_token
from google.auth.transport import requests from google.auth.transport import requests
from configs import Config from configs import Config
from models import UserEntity
class AuthService: class AuthService:
@ -13,28 +17,48 @@ class AuthService:
def verify_google_id_token(self, id_token_str): def verify_google_id_token(self, id_token_str):
try: try:
# Verifikasi token Google
payload = id_token.verify_oauth2_token( payload = id_token.verify_oauth2_token(
id_token_str, requests.Request(), Config.GOOGLE_CLIENT_ID 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 = { google_id = payload.get("sub")
"_id": payload.get("sub"), email = payload.get("email")
"email": payload.get("email"),
"name": payload.get("name"), existing_user = self.user_repository.get_by_google_id(google_id)
"picture": payload.get("picture"), if existing_user:
"given_name": payload.get("given_name"), if existing_user.email == email:
"family_name": payload.get("family_name"), return existing_user
"locale": payload.get("locale"),
"email_verified": payload.get("email_verified", False), new_user = UserEntity(
"iat": payload.get("iat"), id=str(google_id),
"exp": payload.get("exp"), 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: 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 return None
def login(self, data: LoginSchema): def login(self, data: LoginSchema):
@ -43,17 +67,20 @@ class AuthService:
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: 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: if user_data["password"] == data.password:
del user_data["password"] del user_data["password"]
return ApiResponse( # return ApiResponse(
success=True, message="Login success", data=user_data # success=True, message="Login success", data=user_data
) # )
return None
return ApiResponse(success=False, message="Invalid password", data=None) # return ApiResponse(success=False, message="Invalid password", data=None)
return None
except Exception as e: except Exception as e:
print(f"the issue is {e}") print(f"the issue is {e}")
return ApiResponse( # return ApiResponse(
success=False, message="Internal server error", data=None # success=False, message="Internal server error", data=None
) # )
return None