[33e88f42] Keine Zusammenfassung angegeben.
Keine Zusammenfassung angegeben.
This commit is contained in:
129
fotograf-de-scraper/backend/gmail_service.py
Normal file
129
fotograf-de-scraper/backend/gmail_service.py
Normal file
@@ -0,0 +1,129 @@
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import datetime
|
||||
from typing import Optional, List, Dict, Any
|
||||
from google.oauth2.credentials import Credentials
|
||||
from google_auth_oauthlib.flow import Flow
|
||||
from googleapiclient.discovery import build
|
||||
from google.auth.transport.requests import Request
|
||||
from sqlalchemy.orm import Session
|
||||
from database import GmailToken
|
||||
import base64
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
logger = logging.getLogger("gmail-service")
|
||||
|
||||
# Scopes required for sending emails
|
||||
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
|
||||
|
||||
class GmailService:
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
self.client_id = os.getenv("google_fotograf_client_id")
|
||||
self.client_secret = os.getenv("google_fotograf_secret")
|
||||
|
||||
# Redirect URI - must match what was configured in Google Console
|
||||
# We try to detect the public URL, fallback to duckdns
|
||||
self.redirect_uri = os.getenv("GOOGLE_REDIRECT_URI", "https://floke-ai.duckdns.org/fotograf-de-api/api/auth/callback")
|
||||
|
||||
def _get_client_config(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"web": {
|
||||
"client_id": self.client_id,
|
||||
"project_id": "fotograf-tool",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_secret": self.client_secret,
|
||||
"redirect_uris": [self.redirect_uri]
|
||||
}
|
||||
}
|
||||
|
||||
def get_auth_url(self) -> str:
|
||||
flow = Flow.from_client_config(
|
||||
self._get_client_config(),
|
||||
scopes=SCOPES,
|
||||
redirect_uri=self.redirect_uri
|
||||
)
|
||||
auth_url, _ = flow.authorization_url(prompt='consent', access_type='offline')
|
||||
return auth_url
|
||||
|
||||
def handle_callback(self, code: str):
|
||||
flow = Flow.from_client_config(
|
||||
self._get_client_config(),
|
||||
scopes=SCOPES,
|
||||
redirect_uri=self.redirect_uri
|
||||
)
|
||||
flow.fetch_token(code=code)
|
||||
credentials = flow.credentials
|
||||
self._save_token(credentials)
|
||||
return credentials
|
||||
|
||||
def _save_token(self, credentials):
|
||||
token_data = {
|
||||
'token': credentials.token,
|
||||
'refresh_token': credentials.refresh_token,
|
||||
'token_uri': credentials.token_uri,
|
||||
'client_id': credentials.client_id,
|
||||
'client_secret': credentials.client_secret,
|
||||
'scopes': credentials.scopes
|
||||
}
|
||||
|
||||
db_token = self.db.query(GmailToken).first()
|
||||
if not db_token:
|
||||
db_token = GmailToken(token_json=json.dumps(token_data))
|
||||
self.db.add(db_token)
|
||||
else:
|
||||
db_token.token_json = json.dumps(token_data)
|
||||
|
||||
self.db.commit()
|
||||
logger.info("Gmail OAuth token saved to database.")
|
||||
|
||||
def get_credentials(self) -> Optional[Credentials]:
|
||||
db_token = self.db.query(GmailToken).first()
|
||||
if not db_token:
|
||||
return None
|
||||
|
||||
token_data = json.loads(db_token.token_json)
|
||||
creds = Credentials.from_authorized_user_info(token_data, SCOPES)
|
||||
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
logger.info("Gmail token expired, refreshing...")
|
||||
creds.refresh(Request())
|
||||
self._save_token(creds)
|
||||
|
||||
return creds
|
||||
|
||||
def is_authenticated(self) -> bool:
|
||||
try:
|
||||
creds = self.get_credentials()
|
||||
return creds is not None and creds.valid
|
||||
except Exception as e:
|
||||
logger.error(f"Auth check failed: {e}")
|
||||
return False
|
||||
|
||||
def send_email(self, to: str, subject: str, body_html: str) -> bool:
|
||||
creds = self.get_credentials()
|
||||
if not creds:
|
||||
logger.error("Cannot send email: Not authenticated.")
|
||||
return False
|
||||
|
||||
try:
|
||||
service = build('gmail', 'v1', credentials=creds)
|
||||
message = MIMEText(body_html, 'html')
|
||||
message['to'] = to
|
||||
message['subject'] = subject
|
||||
|
||||
raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
|
||||
|
||||
send_result = service.users().messages().send(
|
||||
userId='me',
|
||||
body={'raw': raw_message}
|
||||
).execute()
|
||||
|
||||
logger.info(f"Email sent to {to}. Message ID: {send_result['id']}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send email to {to}: {e}")
|
||||
return False
|
||||
Reference in New Issue
Block a user