feat: replace SendGrid with Gmail API via Domain-Wide Delegation
Some checks are pending
Check Python Version Consistency / Check Python Version (push) Waiting to run
Some checks are pending
Check Python Version Consistency / Check Python Version (push) Waiting to run
This commit is contained in:
parent
e744a1d510
commit
bc0ee3b3ee
50
main.py
50
main.py
|
|
@ -57,8 +57,7 @@ from functools import wraps
|
|||
from starlette.middleware.sessions import SessionMiddleware
|
||||
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
||||
from authlib.integrations.starlette_client import OAuth
|
||||
import sendgrid
|
||||
from sendgrid.helpers.mail import Mail
|
||||
|
||||
|
||||
AGENT_ID = "osvauco-opax"
|
||||
PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT", "propane-will-491900-m5")
|
||||
|
|
@ -305,7 +304,7 @@ async def vm_ssh_key(instance: str = "osvauco-dev-vm"):
|
|||
@app.get("/notify/channels")
|
||||
async def get_notify_channels():
|
||||
return {"channels": [
|
||||
{"type": "email", "configured": bool(os.getenv("SENDGRID_API_KEY")), "target": os.getenv("NOTIFY_EMAIL_TO")},
|
||||
{"type": "email", "configured": bool(os.getenv("GMAIL_DEFAULT_SENDER")), "target": os.getenv("NOTIFY_EMAIL_TO")},
|
||||
{"type": "sms", "configured": bool(os.getenv("TWILIO_ACCOUNT_SID")), "target": os.getenv("TWILIO_FROM_NUMBER")},
|
||||
{"type": "webhook", "configured": bool(os.getenv("NOTIFY_WEBHOOK_URL")), "url": os.getenv("NOTIFY_WEBHOOK_URL")},
|
||||
]}
|
||||
|
|
@ -473,20 +472,49 @@ class NotificationService:
|
|||
return {"status": "error", "detail": str(e)}
|
||||
|
||||
async def send_email(self, req) -> dict:
|
||||
api_key = os.environ.get("SENDGRID_API_KEY")
|
||||
import base64
|
||||
import os
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from google.auth import default as google_auth_default
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
to_email = req.to or ALERT_EMAIL
|
||||
if not api_key:
|
||||
return {"status": "not_configured", "detail": "SENDGRID_API_KEY mangler"}
|
||||
if not to_email:
|
||||
return {"status": "not_configured", "detail": "Ingen mottaker-e-post"}
|
||||
return {"status": "skipped", "reason": "no recipient"}
|
||||
|
||||
allowed_senders_str = os.getenv("GMAIL_ALLOWED_SENDERS", "")
|
||||
allowed = [s.strip() for s in allowed_senders_str.split(",") if s.strip()]
|
||||
sender = os.getenv("GMAIL_DEFAULT_SENDER")
|
||||
|
||||
if not sender or not allowed:
|
||||
return {"status": "not_configured", "reason": "GMAIL_DEFAULT_SENDER or GMAIL_ALLOWED_SENDERS not set"}
|
||||
|
||||
if sender not in allowed:
|
||||
return {"status": "error", "reason": f"Default sender {sender} is not in the allowed list"}
|
||||
|
||||
meta = self._event_meta(req.event)
|
||||
subject = req.subject or f"{meta['emoji']} CostGuard: {meta['label']} — {datetime.date.today()}"
|
||||
html_content = req.body_html if req.body_html else await self._build_digest_html(req.payload)
|
||||
|
||||
try:
|
||||
message = Mail(from_email="costguard@osvauco.no", to_emails=to_email, subject=subject, html_content=html_content)
|
||||
sg = sendgrid.SendGridAPIClient(api_key)
|
||||
response = sg.send(message)
|
||||
return {"status": "ok", "message_id": response.headers.get("X-Message-Id", ""), "to": to_email, "event": req.event}
|
||||
credentials, _ = google_auth_default(scopes=["https://www.googleapis.com/auth/gmail.send"])
|
||||
credentials = credentials.with_subject(sender)
|
||||
|
||||
service = build("gmail", "v1", credentials=credentials)
|
||||
|
||||
message = MIMEMultipart("alternative")
|
||||
message["to"] = to_email
|
||||
message["from"] = sender
|
||||
message["subject"] = subject
|
||||
message.attach(MIMEText(html_content, "html"))
|
||||
|
||||
raw = base64.urlsafe_b64encode(message.as_bytes()).decode()
|
||||
result = service.users().messages().send(
|
||||
userId="me", body={"raw": raw}
|
||||
).execute()
|
||||
|
||||
return {"status": "ok", "message_id": result.get("id", ""), "to": to_email, "event": req.event}
|
||||
except Exception as e:
|
||||
return {"status": "error", "detail": str(e)}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user