- Add email verification with token-based validation - Integrate Google, Facebook, and Yahoo OAuth providers - Add OAuth configuration and email service modules - Update User model with email_verified, oauth_provider, oauth_id fields - Implement async password hashing/verification to prevent blocking - Add database migration script for new user fields - Create email verification page with professional UI - Update login page with social login buttons (Google, Facebook, Yahoo) - Add OAuth callback token handling - Implement scroll-to-top navigation component - Add 5-second real-time polling for Products and Services pages - Enhance About page with Apple-style scroll animations - Update Home and Contact pages with branding and business info - Optimize API cache with prefix-based clearing - Create comprehensive setup documentation and quick start guide - Fix login performance with ThreadPoolExecutor for bcrypt operations Performance improvements: - Login time optimized to ~220ms with async password verification - Real-time data updates every 5 seconds - Non-blocking password operations Security enhancements: - Email verification required for new accounts - OAuth integration for secure social login - Verification tokens expire after 24 hours - Password field nullable for OAuth users
228 lines
9.4 KiB
Python
228 lines
9.4 KiB
Python
"""
|
|
Email Service for PromptTech Solutions
|
|
Handles email verification, notifications, and password resets
|
|
"""
|
|
import smtplib
|
|
import os
|
|
from email.mime.text import MIMEText
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.image import MIMEImage
|
|
from pathlib import Path
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Email configuration from environment
|
|
SMTP_HOST = os.environ.get('SMTP_HOST', 'smtp.gmail.com')
|
|
SMTP_PORT = int(os.environ.get('SMTP_PORT', 587))
|
|
SMTP_USER = os.environ.get('SMTP_USER', '')
|
|
SMTP_PASSWORD = os.environ.get('SMTP_PASSWORD', '')
|
|
FROM_EMAIL = os.environ.get('FROM_EMAIL', SMTP_USER)
|
|
FRONTEND_URL = os.environ.get('FRONTEND_URL', 'http://localhost:5300')
|
|
|
|
|
|
def send_email(to_email: str, subject: str, html_content: str, text_content: str = None):
|
|
"""
|
|
Send an email using SMTP
|
|
|
|
Args:
|
|
to_email: Recipient email address
|
|
subject: Email subject
|
|
html_content: HTML content of the email
|
|
text_content: Plain text fallback (optional)
|
|
"""
|
|
if not SMTP_USER or not SMTP_PASSWORD:
|
|
logger.warning("SMTP credentials not configured. Email not sent.")
|
|
return False
|
|
|
|
try:
|
|
# Create message
|
|
message = MIMEMultipart('alternative')
|
|
message['Subject'] = subject
|
|
message['From'] = f"PromptTech Solutions <{FROM_EMAIL}>"
|
|
message['To'] = to_email
|
|
|
|
# Add text/plain part (fallback)
|
|
if text_content:
|
|
text_part = MIMEText(text_content, 'plain')
|
|
message.attach(text_part)
|
|
|
|
# Add text/html part
|
|
html_part = MIMEText(html_content, 'html')
|
|
message.attach(html_part)
|
|
|
|
# Send email
|
|
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
|
|
server.starttls()
|
|
server.login(SMTP_USER, SMTP_PASSWORD)
|
|
server.send_message(message)
|
|
|
|
logger.info(f"Email sent successfully to {to_email}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to send email to {to_email}: {e}")
|
|
return False
|
|
|
|
|
|
def send_verification_email(to_email: str, first_name: str, verification_token: str):
|
|
"""Send email verification link to new user"""
|
|
verification_link = f"{FRONTEND_URL}/verify-email?token={verification_token}"
|
|
|
|
subject = "Verify your PromptTech Solutions account"
|
|
|
|
html_content = f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
|
|
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; }}
|
|
.header {{ background-color: #4F46E5; color: white; padding: 20px; text-align: center; border-radius: 8px 8px 0 0; }}
|
|
.content {{ background-color: #f9fafb; padding: 30px; border-radius: 0 0 8px 8px; }}
|
|
.button {{ display: inline-block; background-color: #4F46E5; color: white; padding: 12px 30px; text-decoration: none; border-radius: 6px; margin: 20px 0; }}
|
|
.footer {{ text-align: center; margin-top: 30px; color: #666; font-size: 12px; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>Welcome to PromptTech Solutions!</h1>
|
|
</div>
|
|
<div class="content">
|
|
<p>Hi {first_name},</p>
|
|
<p>Thank you for creating an account with PromptTech Solutions. To complete your registration and verify your email address, please click the button below:</p>
|
|
<div style="text-align: center;">
|
|
<a href="{verification_link}" class="button">Verify Email Address</a>
|
|
</div>
|
|
<p>Or copy and paste this link into your browser:</p>
|
|
<p style="word-break: break-all; color: #4F46E5;">{verification_link}</p>
|
|
<p><strong>This link will expire in 24 hours.</strong></p>
|
|
<p>If you didn't create this account, you can safely ignore this email.</p>
|
|
<p>Best regards,<br>The PromptTech Solutions Team</p>
|
|
</div>
|
|
<div class="footer">
|
|
<p>© 2026 PromptTech Solutions. All rights reserved.</p>
|
|
<p>Belmopan City, Belize | (501) 638-6318 | prompttechbz@gmail.com</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
text_content = f"""
|
|
Hi {first_name},
|
|
|
|
Thank you for creating an account with PromptTech Solutions. To complete your registration and verify your email address, please visit:
|
|
|
|
{verification_link}
|
|
|
|
This link will expire in 24 hours.
|
|
|
|
If you didn't create this account, you can safely ignore this email.
|
|
|
|
Best regards,
|
|
The PromptTech Solutions Team
|
|
"""
|
|
|
|
return send_email(to_email, subject, html_content, text_content)
|
|
|
|
|
|
def send_welcome_email(to_email: str, first_name: str):
|
|
"""Send welcome email after successful verification"""
|
|
subject = "Welcome to PromptTech Solutions!"
|
|
|
|
html_content = f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
|
|
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; }}
|
|
.header {{ background-color: #10B981; color: white; padding: 20px; text-align: center; border-radius: 8px 8px 0 0; }}
|
|
.content {{ background-color: #f9fafb; padding: 30px; border-radius: 0 0 8px 8px; }}
|
|
.button {{ display: inline-block; background-color: #4F46E5; color: white; padding: 12px 30px; text-decoration: none; border-radius: 6px; margin: 10px 5px; }}
|
|
.footer {{ text-align: center; margin-top: 30px; color: #666; font-size: 12px; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>🎉 Account Verified!</h1>
|
|
</div>
|
|
<div class="content">
|
|
<p>Hi {first_name},</p>
|
|
<p>Your email has been successfully verified! You now have full access to your PromptTech Solutions account.</p>
|
|
<h3>What's Next?</h3>
|
|
<ul>
|
|
<li>Browse our latest electronics and tech products</li>
|
|
<li>Book professional repair services</li>
|
|
<li>Add items to your cart and checkout securely</li>
|
|
<li>Track your orders in real-time</li>
|
|
</ul>
|
|
<div style="text-align: center; margin-top: 30px;">
|
|
<a href="{FRONTEND_URL}/products" class="button">Shop Now</a>
|
|
<a href="{FRONTEND_URL}/services" class="button">View Services</a>
|
|
</div>
|
|
<p style="margin-top: 30px;">Need help? Contact us at <a href="mailto:prompttechbz@gmail.com">prompttechbz@gmail.com</a> or call (501) 638-6318</p>
|
|
<p>Best regards,<br>The PromptTech Solutions Team</p>
|
|
</div>
|
|
<div class="footer">
|
|
<p>© 2026 PromptTech Solutions. All rights reserved.</p>
|
|
<p>Belmopan City, Belize | Mon-Fri: 8AM-5PM | Sat: 9AM-5PM</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
return send_email(to_email, subject, html_content)
|
|
|
|
|
|
def send_password_reset_email(to_email: str, first_name: str, reset_token: str):
|
|
"""Send password reset link"""
|
|
reset_link = f"{FRONTEND_URL}/reset-password?token={reset_token}"
|
|
|
|
subject = "Reset your PromptTech Solutions password"
|
|
|
|
html_content = f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
|
|
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; }}
|
|
.header {{ background-color: #EF4444; color: white; padding: 20px; text-align: center; border-radius: 8px 8px 0 0; }}
|
|
.content {{ background-color: #f9fafb; padding: 30px; border-radius: 0 0 8px 8px; }}
|
|
.button {{ display: inline-block; background-color: #EF4444; color: white; padding: 12px 30px; text-decoration: none; border-radius: 6px; margin: 20px 0; }}
|
|
.footer {{ text-align: center; margin-top: 30px; color: #666; font-size: 12px; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>🔒 Password Reset Request</h1>
|
|
</div>
|
|
<div class="content">
|
|
<p>Hi {first_name},</p>
|
|
<p>We received a request to reset your password. Click the button below to create a new password:</p>
|
|
<div style="text-align: center;">
|
|
<a href="{reset_link}" class="button">Reset Password</a>
|
|
</div>
|
|
<p>Or copy and paste this link into your browser:</p>
|
|
<p style="word-break: break-all; color: #EF4444;">{reset_link}</p>
|
|
<p><strong>This link will expire in 1 hour.</strong></p>
|
|
<p>If you didn't request a password reset, please ignore this email and your password will remain unchanged.</p>
|
|
<p>Best regards,<br>The PromptTech Solutions Team</p>
|
|
</div>
|
|
<div class="footer">
|
|
<p>© 2026 PromptTech Solutions. All rights reserved.</p>
|
|
<p>Belmopan City, Belize | (501) 638-6318 | prompttechbz@gmail.com</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
return send_email(to_email, subject, html_content)
|