Automated Action 55f5e5f5a8 Add comprehensive AI video dubbing features with Docker support
- Auto language detection using OpenAI Whisper during video upload
- Editable transcript interface for reviewing/correcting transcriptions
- Updated translation pipeline to use edited transcripts when available
- Migrated from JWT to Google OAuth-only authentication for better security
- Added complete Docker containerization with docker-compose.yml
- Updated database schema with language detection and transcript editing fields
- Enhanced API documentation and workflow in README
- Added comprehensive environment variable configuration

🤖 Generated with BackendIM

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-28 08:12:00 +00:00

231 lines
8.0 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
import logging
from app.db.session import get_db
from app.models.user import User
from app.services.google_oauth_service import GoogleOAuthService
logger = logging.getLogger(__name__)
router = APIRouter()
# Google OAuth Models
class GoogleTokenLogin(BaseModel):
id_token: str
class GoogleCodeLogin(BaseModel):
code: str
redirect_uri: str
class GoogleOAuthURL(BaseModel):
oauth_url: str
class GoogleAuthResponse(BaseModel):
google_token: str
token_type: str = "bearer"
user: dict
class GoogleUserResponse(BaseModel):
id: int
email: str
first_name: Optional[str]
last_name: Optional[str]
full_name: str
is_google_user: bool
email_verified: bool
profile_picture: Optional[str]
created_at: str
is_new_user: bool
class Config:
orm_mode = True
# Google OAuth Endpoints
@router.get("/google/oauth-url", response_model=GoogleOAuthURL)
async def get_google_oauth_url():
"""Get Google OAuth URL for frontend redirect"""
oauth_url = GoogleOAuthService.get_google_oauth_url()
if not oauth_url:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Google OAuth not configured"
)
return {"oauth_url": oauth_url}
@router.post("/google/login-with-token", response_model=GoogleAuthResponse)
async def google_login_with_token(
google_data: GoogleTokenLogin,
db: Session = Depends(get_db)
):
"""Login/Register with Google ID token"""
try:
logger.info("Google OAuth login attempt with ID token")
# Verify Google token
user_info = await GoogleOAuthService.verify_google_token(google_data.id_token)
if not user_info:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid Google token"
)
# Check if user exists by email
db_user = db.query(User).filter(User.email == user_info['email']).first()
if db_user:
# Existing user - update Google info if not already a Google user
if not db_user.is_google_user:
db_user.google_id = user_info['google_id']
db_user.is_google_user = True
db_user.email_verified = user_info['email_verified']
if user_info.get('picture'):
db_user.profile_picture = user_info['picture']
if user_info.get('first_name') and not db_user.first_name:
db_user.first_name = user_info['first_name']
if user_info.get('last_name') and not db_user.last_name:
db_user.last_name = user_info['last_name']
db_user.updated_at = datetime.utcnow()
db.commit()
db.refresh(db_user)
logger.info(f"Updated existing user with Google OAuth: {db_user.email}")
else:
# New user - create account
db_user = User(
email=user_info['email'],
google_id=user_info['google_id'],
is_google_user=True,
email_verified=user_info['email_verified'],
first_name=user_info.get('first_name'),
last_name=user_info.get('last_name'),
profile_picture=user_info.get('picture'),
preferred_language=user_info.get('locale', 'en')
)
db.add(db_user)
db.commit()
db.refresh(db_user)
logger.info(f"Created new Google user: {db_user.email}")
# Return Google token directly for authentication
return {
"google_token": google_data.id_token,
"token_type": "bearer",
"user": {
"id": db_user.id,
"email": db_user.email,
"first_name": db_user.first_name,
"last_name": db_user.last_name,
"is_google_user": db_user.is_google_user,
"email_verified": db_user.email_verified,
"profile_picture": db_user.profile_picture,
"created_at": str(db_user.created_at)
}
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Google OAuth error: {str(e)}")
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Internal server error during Google authentication"
)
@router.post("/google/login-with-code", response_model=GoogleAuthResponse)
async def google_login_with_code(
google_data: GoogleCodeLogin,
db: Session = Depends(get_db)
):
"""Login/Register with Google authorization code"""
try:
logger.info("Google OAuth login attempt with authorization code")
# Exchange code for ID token
id_token = await GoogleOAuthService.exchange_code_for_token(
google_data.code,
google_data.redirect_uri
)
if not id_token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Failed to exchange authorization code"
)
# Verify the ID token and process user
user_info = await GoogleOAuthService.verify_google_token(id_token)
if not user_info:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid Google token"
)
# Same logic as token login
db_user = db.query(User).filter(User.email == user_info['email']).first()
if db_user:
if not db_user.is_google_user:
db_user.google_id = user_info['google_id']
db_user.is_google_user = True
db_user.email_verified = user_info['email_verified']
if user_info.get('picture'):
db_user.profile_picture = user_info['picture']
if user_info.get('first_name') and not db_user.first_name:
db_user.first_name = user_info['first_name']
if user_info.get('last_name') and not db_user.last_name:
db_user.last_name = user_info['last_name']
db_user.updated_at = datetime.utcnow()
db.commit()
db.refresh(db_user)
else:
db_user = User(
email=user_info['email'],
google_id=user_info['google_id'],
is_google_user=True,
email_verified=user_info['email_verified'],
first_name=user_info.get('first_name'),
last_name=user_info.get('last_name'),
profile_picture=user_info.get('picture'),
preferred_language=user_info.get('locale', 'en')
)
db.add(db_user)
db.commit()
db.refresh(db_user)
# Return Google token directly for authentication
return {
"google_token": id_token,
"token_type": "bearer",
"user": {
"id": db_user.id,
"email": db_user.email,
"first_name": db_user.first_name,
"last_name": db_user.last_name,
"is_google_user": db_user.is_google_user,
"email_verified": db_user.email_verified,
"profile_picture": db_user.profile_picture,
"created_at": str(db_user.created_at)
}
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Google OAuth code exchange error: {str(e)}")
db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Internal server error during Google authentication"
)