Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions api/google_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import datetime
import os
import logging
from typing import Optional
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse, RedirectResponse
import httpx
from fastapi.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
from bson import ObjectId
from user_manager import UserManager
from pydantic import BaseModel

# Configuration de logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

load_dotenv()

app = FastAPI()

mongo_client = AsyncIOMotorClient(os.getenv("url"))
db = mongo_client["shipfast"]

google_client_id = os.getenv("google_client_id")
google_client_secret = os.getenv("google_client_secret")
google_redirect_uri = os.getenv("google_redirect_uri")

# En local
origins = ["http://localhost:4200"]

# En production
# origins = ["https://ship-faster.netlify.app"]

app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

class GoogleUser(BaseModel):
username: Optional[str]
name: str
email: str
come_from: str
verified_email: str
creation_month: str
creation_year: str


def serialize_doc(doc):
"""Convert MongoDB document to serializable format"""
if isinstance(doc, ObjectId):
return str(doc)
if isinstance(doc, dict):
return {k: serialize_doc(v) for k, v in doc.items()}
if isinstance(doc, list):
return [serialize_doc(i) for i in doc]
return doc

class GoogleManager:
def __init__(self, db):
self.db = db
self.user_manager = UserManager(db) # Initialise le gestionnaire utilisateur

async def save_or_login_google_user(self, user_data):
try:
# Vérifiez si l'utilisateur existe déjà par email
existing_user = await self.db.users.find_one({"email": user_data["email"]})

if existing_user:
logger.info("Email already registered: %s", user_data["email"])

# Générer un JWT pour l'utilisateur existant
token_data = {"sub": existing_user["email"]}
expires = datetime.timedelta(days=30)
access_token = self.user_manager.create_jwt_token(existing_user, expires)

# Retournez les informations de l'utilisateur existant avec le token de session
return {"user": serialize_doc(existing_user), "access_token": access_token}

# Si l'utilisateur n'existe pas, créez un nouveau compte
creation_date = datetime.datetime.utcnow()
user_data["come_from"] = "google"
user_data["creation_month"] = creation_date.strftime("%B")
user_data["creation_year"] = creation_date.year

# Insérez l'utilisateur dans la base de données
# result = await self.db.users.insert_one(user_data)
# logger.info("User created with ID: %s", result.inserted_id)

# Générer un JWT pour le nouvel utilisateur
token_data = {"sub": user_data["email"]}
expires = datetime.timedelta(days=30)
access_token = self.user_manager.create_jwt_token(token_data, expires)

# Retournez les informations du nouvel utilisateur avec le token de session
return {"user": user_data, "access_token": access_token}
except Exception as e:
logger.error("Error saving Google user: %s", str(e))
raise HTTPException(
status_code=500,
detail=f"Error saving Google user: {str(e)}"
)

async def google_user_info(self, access_token: str):
# Vérifier si le token est toujours valide
token_info_url = f"https://oauth2.googleapis.com/tokeninfo?access_token={access_token}"
async with httpx.AsyncClient() as client:
token_info_response = await client.get(token_info_url)

logger.info(f"Token info response status: {token_info_response.status_code}")
logger.info(f"Token info response body: {token_info_response.text}")

if token_info_response.status_code != 200:
raise HTTPException(status_code=401, detail="Invalid or expired access token")

# Continuer à utiliser le jeton pour récupérer les informations de l'utilisateur
user_info_url = "https://www.googleapis.com/oauth2/v1/userinfo"
headers = {
"Authorization": f"Bearer {access_token}"
}
async with httpx.AsyncClient() as client:
user_info_response = await client.get(user_info_url, headers=headers)

logger.info(f"User info response status: {user_info_response.status_code}")
logger.info(f"User info response body: {user_info_response.text}")

if user_info_response.status_code != 200:
raise HTTPException(status_code=401, detail="Unauthorized request to Google API")

user_info = user_info_response.json()

# Préparer les données utilisateur
user_data = {
"name": user_info.get("name"),
"email": user_info.get("email"),
"come_from": "google",
"verified_email": user_info.get("verified_email"),
}

# Enregistrer ou connecter l'utilisateur
user = await self.save_or_login_google_user(user_data)

return serialize_doc(user)


# Initialisation de GoogleManager
google_manager_instance = GoogleManager(db)


@app.get("/api/google-login")
async def google_login():
google_auth_endpoint = "https://accounts.google.com/o/oauth2/v2/auth"
params = {
"client_id": google_client_id,
"response_type": "code",
"redirect_uri": google_redirect_uri,
"scope": "openid email profile",
"access_type": "offline",
"prompt": "consent",
}
url = f"{google_auth_endpoint}?{'&'.join([f'{key}={value}' for key, value in params.items()])}"
return RedirectResponse(url)


@app.get("/api/google-portal")
async def google_portal(request: Request):
code = request.query_params.get("code")
if not code:
raise HTTPException(status_code=400, detail="Authorization code not provided")

token_url = "https://oauth2.googleapis.com/token"

token_data = {
"code": code,
"client_id": google_client_id,
"client_secret": google_client_secret,
"redirect_uri": google_redirect_uri,
"grant_type": "authorization_code",
}

headers = {"Content-Type": "application/x-www-form-urlencoded"}
async with httpx.AsyncClient() as client:
token_response = await client.post(token_url, data=token_data, headers=headers)

token_response_json = token_response.json()

if token_response.status_code != 200:
raise HTTPException(status_code=token_response.status_code, detail="Failed to obtain access token from Google")

access_token = token_response_json.get("access_token")
id_token = token_response_json.get("id_token")

if not access_token or not id_token:
raise HTTPException(
status_code=400, detail="Failed to obtain valid tokens from Google"
)

# Utilisez uniquement le jeton d'accès reçu pour valider et obtenir les infos utilisateur
user_data = await google_manager_instance.google_user_info(access_token)

# Retournez les informations utilisateur avec le token de session
return JSONResponse(content=user_data)

@app.post("/api/google-save-user")
async def save_user(user: GoogleUser):
print(user)
try:
# Convertir le modèle en dictionnaire
user_data = user.dict()
print(user_data)

# Insérer l'utilisateur dans la base de données
result = await db.users.insert_one(user_data)
logger.info("User created with ID: %s", result.inserted_id)

# Retourner une réponse JSON avec l'ID de l'utilisateur inséré
return JSONResponse(content={"id": str(result.inserted_id)}, status_code=201)
except Exception as e:
logger.error("Error saving user: %s", str(e))
raise HTTPException(
status_code=500,
detail=f"Error saving user: {str(e)}"
)
5 changes: 5 additions & 0 deletions api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@
from pydantic import BaseModel
from motor.motor_asyncio import AsyncIOMotorClient
from github_manager import app as github_manager
from google_manager import app as google_manager

load_dotenv()

app = FastAPI()

app.include_router(github_manager.router)
app.include_router(google_manager.router)

mongo_client = AsyncIOMotorClient(os.getenv("url"))
db = mongo_client["shipfast"]
github_client_id = os.getenv("github_client_id")
github_client_secret = os.getenv("github_client_secret")
google_client_id = os.getenv("google_client_id")
google_client_secret = os.getenv("google_client_secret")
google_redirect_uri = os.getenv("google_redirect_uri")

user_manager = UserManager(db)

Expand Down
7 changes: 6 additions & 1 deletion app/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes, ActivatedRoute } from '@angular/router';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './components/home/home/home.component';
import { SignupComponent } from './components/auth/signup/signup.component';
import { ProfileGuard } from './guard/profile.guard';
Expand All @@ -8,6 +8,7 @@ import { NoAuthGuard } from './guard/no-auth.guard';
import { UsernameCreationComponent } from './components/auth/username-creation/username-creation.component';
import { GithubPortalComponent } from './components/portal/github-portal/github-portal.component';
import { ProfileComponent } from './components/profile/profile/profile.component';
import { GooglePortalComponent } from './components/portal/google-portal/google-portal.component';

const routes: Routes = [
{
Expand All @@ -18,6 +19,10 @@ const routes: Routes = [
path: 'github-portal',
component: GithubPortalComponent,
},
{
path: 'google-portal',
component: GooglePortalComponent,
},
{
path: 'auth',
children: [
Expand Down
4 changes: 3 additions & 1 deletion app/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ProfileComponent } from './components/profile/profile/profile.component
import { UsernameCreationComponent } from './components/auth/username-creation/username-creation.component';
import { GithubPortalComponent } from './components/portal/github-portal/github-portal.component';
import { EditProfileComponent } from './components/profile/edit-profile/edit-profile.component';
import { GooglePortalComponent } from './components/portal/google-portal/google-portal.component';

@NgModule({
declarations: [
Expand All @@ -25,7 +26,8 @@ import { EditProfileComponent } from './components/profile/edit-profile/edit-pro
GithubPortalComponent,
ProfileComponent,
DisplayProfileComponent,
EditProfileComponent
EditProfileComponent,
GooglePortalComponent
],
imports: [
BrowserModule,
Expand Down
4 changes: 2 additions & 2 deletions app/src/app/components/auth/login/login.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
<img src="../../../../assets/apple.png" alt="Apple logo" class="w-5 h-5">
</button>

<button (click)="openLogin()" class="bg-gray-900 hover:bg-gray-900 text-white font-bold py-3 px-8 rounded-lg">
<button (click)="githubAuth()" class="bg-gray-900 hover:bg-gray-900 text-white font-bold py-3 px-8 rounded-lg">
<img src="../../../../assets/github.png" alt="GitHub logo" class="w-5 h-5">
</button>

<button class="bg-gray-900 hover:bg-gray-8900 text-white font-bold py-3 px-8 rounded-lg">
<button (click)="googleAuth()" class="bg-gray-900 hover:bg-gray-8900 text-white font-bold py-3 px-8 rounded-lg">
<img src="../../../../assets/google.png" alt="Google logo" class="w-5 h-5">
</button>
</div>
Expand Down
9 changes: 7 additions & 2 deletions app/src/app/components/auth/login/login.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AuthService } from 'src/app/services/auth.service';
import { AuthReceiveLoginUser } from 'src/app/models/login-user.model';
import { AuthService } from 'src/app/services/auth/auth.service';
import { environment } from 'src/environments/environment.development';

@Component({
Expand Down Expand Up @@ -63,11 +63,16 @@ export class LoginComponent {
}, 100);
}

openLogin() {
githubAuth() {
const url = `${environment.apiUrl}/api/github-login`
window.location.href = url;
}

googleAuth() {
const url = `${environment.apiUrl}/api/google-login`
window.location.href = url;
}

localUser(data: any) {
this.name = data.name;
this.username = data.username;
Expand Down
3 changes: 1 addition & 2 deletions app/src/app/components/auth/signup/signup.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthCreateUser } from 'src/app/models/create-user.model';
import { AuthService } from 'src/app/services/auth.service';
import { AuthService } from 'src/app/services/auth/auth.service';
import { environment } from 'src/environments/environment.development';

@Component({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
</div>
</ng-container>

<ng-container *ngIf="!loader">
<ng-container *ngIf="failure">
<div class="flex h-screen">
<span class="m-auto text-sm pt-2 text-red-500">{{failure}}</span>
</div>
</ng-container>

<ng-container *ngIf="!loader && !failure">
<div class="flex h-screen">
<form [formGroup]="createUsernameForm" class="w-1/3 m-auto">
<div class="mb-4 -mt-20">
Expand Down
Loading