Use Gmail API for OTP email
This commit is contained in:
parent
f39f6390d6
commit
4b60a94ffb
|
|
@ -17,7 +17,8 @@ import secrets
|
||||||
import os
|
import os
|
||||||
import smtplib
|
import smtplib
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from urllib.parse import urlparse
|
from base64 import urlsafe_b64encode
|
||||||
|
from urllib.parse import urlencode, urlparse
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
from mysql.connector import Error
|
from mysql.connector import Error
|
||||||
|
|
@ -98,8 +99,10 @@ SMTP_USE_SSL = os.getenv('SMTP_USE_SSL', '').strip().lower() in ['1', 'true', 'y
|
||||||
SMTP_EMAIL = os.getenv('SMTP_EMAIL', '')
|
SMTP_EMAIL = os.getenv('SMTP_EMAIL', '')
|
||||||
SMTP_PASSWORD = os.getenv('SMTP_APP_PASSWORD', '').replace(' ', '')
|
SMTP_PASSWORD = os.getenv('SMTP_APP_PASSWORD', '').replace(' ', '')
|
||||||
SMTP_SENDER_NAME = os.getenv('SMTP_SENDER_NAME', 'Tobaku Sulastri')
|
SMTP_SENDER_NAME = os.getenv('SMTP_SENDER_NAME', 'Tobaku Sulastri')
|
||||||
BREVO_API_KEY = os.getenv('BREVO_API_KEY', '').strip()
|
GOOGLE_CLIENT_ID = os.getenv('GOOGLE_CLIENT_ID', '').strip()
|
||||||
BREVO_SENDER_EMAIL = os.getenv('BREVO_SENDER_EMAIL', SMTP_EMAIL).strip()
|
GOOGLE_CLIENT_SECRET = os.getenv('GOOGLE_CLIENT_SECRET', '').strip()
|
||||||
|
GOOGLE_REFRESH_TOKEN = os.getenv('GOOGLE_REFRESH_TOKEN', '').strip()
|
||||||
|
GMAIL_SENDER_EMAIL = os.getenv('GMAIL_SENDER_EMAIL', SMTP_EMAIL).strip()
|
||||||
|
|
||||||
# Transaction table names seen across local dumps and deployed databases.
|
# Transaction table names seen across local dumps and deployed databases.
|
||||||
# The current Railway dump uses `stock in`.
|
# The current Railway dump uses `stock in`.
|
||||||
|
|
@ -268,8 +271,8 @@ def hash_password(password: str) -> str:
|
||||||
return hashlib.sha256(password.encode('utf-8')).hexdigest()
|
return hashlib.sha256(password.encode('utf-8')).hexdigest()
|
||||||
|
|
||||||
def send_otp_email(recipient_email: str, otp_code: str) -> None:
|
def send_otp_email(recipient_email: str, otp_code: str) -> None:
|
||||||
if BREVO_API_KEY:
|
if GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET and GOOGLE_REFRESH_TOKEN:
|
||||||
send_otp_email_brevo(recipient_email, otp_code)
|
send_otp_email_gmail_api(recipient_email, otp_code)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not SMTP_EMAIL or not SMTP_PASSWORD:
|
if not SMTP_EMAIL or not SMTP_PASSWORD:
|
||||||
|
|
@ -302,41 +305,64 @@ Tobaku Sulastri
|
||||||
server.login(SMTP_EMAIL, SMTP_PASSWORD)
|
server.login(SMTP_EMAIL, SMTP_PASSWORD)
|
||||||
server.send_message(message)
|
server.send_message(message)
|
||||||
|
|
||||||
def send_otp_email_brevo(recipient_email: str, otp_code: str) -> None:
|
def get_google_access_token() -> str:
|
||||||
if not BREVO_SENDER_EMAIL:
|
|
||||||
raise RuntimeError('BREVO_SENDER_EMAIL atau SMTP_EMAIL belum dikonfigurasi')
|
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
'sender': {
|
'client_id': GOOGLE_CLIENT_ID,
|
||||||
'name': SMTP_SENDER_NAME,
|
'client_secret': GOOGLE_CLIENT_SECRET,
|
||||||
'email': BREVO_SENDER_EMAIL,
|
'refresh_token': GOOGLE_REFRESH_TOKEN,
|
||||||
},
|
'grant_type': 'refresh_token',
|
||||||
'to': [{'email': recipient_email}],
|
|
||||||
'subject': 'Kode OTP Reset Password Tobaku Sulastri',
|
|
||||||
'textContent': (
|
|
||||||
'Halo,\n\n'
|
|
||||||
'Kode OTP untuk reset password aplikasi Tobaku Sulastri adalah:\n\n'
|
|
||||||
f'{otp_code}\n\n'
|
|
||||||
'Kode ini berlaku selama 10 menit. Abaikan email ini jika Anda tidak meminta reset password.\n\n'
|
|
||||||
'Terima kasih,\n'
|
|
||||||
'Tobaku Sulastri\n'
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
token_request = Request(
|
||||||
request = Request(
|
'https://oauth2.googleapis.com/token',
|
||||||
'https://api.brevo.com/v3/smtp/email',
|
data=urlencode(payload).encode('utf-8'),
|
||||||
data=json.dumps(payload).encode('utf-8'),
|
|
||||||
headers={
|
headers={
|
||||||
'accept': 'application/json',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
'api-key': BREVO_API_KEY,
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
},
|
||||||
method='POST',
|
method='POST',
|
||||||
)
|
)
|
||||||
|
with urlopen(token_request, timeout=20) as response:
|
||||||
|
data = json.loads(response.read().decode('utf-8'))
|
||||||
|
|
||||||
with urlopen(request, timeout=20) as response:
|
access_token = data.get('access_token')
|
||||||
|
if not access_token:
|
||||||
|
raise RuntimeError('Gagal mengambil access token Gmail API')
|
||||||
|
return access_token
|
||||||
|
|
||||||
|
def send_otp_email_gmail_api(recipient_email: str, otp_code: str) -> None:
|
||||||
|
if not GMAIL_SENDER_EMAIL:
|
||||||
|
raise RuntimeError('GMAIL_SENDER_EMAIL atau SMTP_EMAIL belum dikonfigurasi')
|
||||||
|
|
||||||
|
message = EmailMessage()
|
||||||
|
message['Subject'] = 'Kode OTP Reset Password Tobaku Sulastri'
|
||||||
|
message['From'] = f'{SMTP_SENDER_NAME} <{GMAIL_SENDER_EMAIL}>'
|
||||||
|
message['To'] = recipient_email
|
||||||
|
message.set_content(
|
||||||
|
f"""Halo,
|
||||||
|
|
||||||
|
Kode OTP untuk reset password aplikasi Tobaku Sulastri adalah:
|
||||||
|
|
||||||
|
{otp_code}
|
||||||
|
|
||||||
|
Kode ini berlaku selama 10 menit. Abaikan email ini jika Anda tidak meminta reset password.
|
||||||
|
|
||||||
|
Terima kasih,
|
||||||
|
Tobaku Sulastri
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
raw_message = urlsafe_b64encode(message.as_bytes()).decode('utf-8').rstrip('=')
|
||||||
|
send_request = Request(
|
||||||
|
'https://gmail.googleapis.com/gmail/v1/users/me/messages/send',
|
||||||
|
data=json.dumps({'raw': raw_message}).encode('utf-8'),
|
||||||
|
headers={
|
||||||
|
'Authorization': f'Bearer {get_google_access_token()}',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
method='POST',
|
||||||
|
)
|
||||||
|
with urlopen(send_request, timeout=20) as response:
|
||||||
if response.status >= 400:
|
if response.status >= 400:
|
||||||
raise RuntimeError(f'Brevo email error: HTTP {response.status}')
|
raise RuntimeError(f'Gmail API send error: HTTP {response.status}')
|
||||||
|
|
||||||
def mask_email(email: str) -> str:
|
def mask_email(email: str) -> str:
|
||||||
if '@' not in email:
|
if '@' not in email:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue