import os import logging from flask import jsonify from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail from database import generate_reset_code, verify_reset_code, update_user_password # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # Email configuration using environment variables EMAIL_ADDRESS = os.environ.get('EMAIL_ADDRESS') SENDGRID_API_KEY = os.environ.get('SENDGRID_API_KEY') SENDER_NAME = os.environ.get('SENDER_NAME', 'BeeGuardian Team') COMPANY_ADDRESS = os.environ.get('COMPANY_ADDRESS', 'Namal University') COMPANY_CITY = os.environ.get('COMPANY_CITY', 'Mianwali') COMPANY_STATE = os.environ.get('COMPANY_STATE', 'Punjab') COMPANY_ZIP = os.environ.get('COMPANY_ZIP', '42200') COMPANY_COUNTRY = os.environ.get('COMPANY_COUNTRY', 'Pakistan') # Send email with reset code using SendGrid def send_reset_email(email, code): if not EMAIL_ADDRESS or not SENDGRID_API_KEY: logger.error("Email configuration missing") return False, "Email configuration missing" # Email content with BeeGuardian intro and CAN-SPAM compliant footer footer = ( f"\n\n--\n" f"{SENDER_NAME}\n" f"{COMPANY_ADDRESS}\n" f"{COMPANY_CITY}, {COMPANY_STATE} {COMPANY_ZIP}\n" f"{COMPANY_COUNTRY}" ) content = ( f"Welcome to BeeGuardian! We are a team of students from Namal University Mianwali who created an innovative app to help beekeepers monitor bee health using mobile phone recordings. Our mission is to protect bees and support biodiversity.\n\n" f"To reset your password, please use this code: {code}\n" f"This code is valid for 15 minutes.{footer}" ) message = Mail( from_email=(EMAIL_ADDRESS, SENDER_NAME), to_emails=email, subject='Password Reset Code for BeeGuardian', plain_text_content=content ) try: sg = SendGridAPIClient(SENDGRID_API_KEY) response = sg.send(message) return True, None except Exception as e: logger.error(f"Failed to send email to {email}: {str(e)}") return False, str(e) # Forgot password logic def forgot_password(request): data = request.json if 'email' not in data: logger.warning("Forgot password request missing email") return jsonify({"error": "Email is required"}), 400 try: code = generate_reset_code(data['email']) if not code: return jsonify({"error": "Email not found"}), 404 success, error_msg = send_reset_email(data['email'], code) if success: return jsonify({"success": True, "message": "Reset code sent to email"}), 200 logger.error(f"Failed to send reset email: {error_msg}") return jsonify({"error": f"Failed to send email: {error_msg}"}), 500 except Exception as e: logger.error(f"Error in forgot_password for {data['email']}: {str(e)}") return jsonify({"error": str(e)}), 500 # Verify reset code logic def verify_reset_code_endpoint(request): data = request.json if not all(key in data for key in ['email', 'code']): logger.warning("Verify reset code request missing email or code") return jsonify({"error": "Email and code are required"}), 400 try: valid = verify_reset_code(data['email'], data['code']) if valid: return jsonify({"success": True, "message": "Code verified"}), 200 return jsonify({"error": "Invalid or expired code"}), 400 except Exception as e: logger.error(f"Error in verify_reset_code for {data['email']}: {str(e)}") return jsonify({"error": str(e)}), 500 # Reset password logic def reset_password(request): data = request.json if not all(key in data for key in ['email', 'new_password']): logger.warning("Reset password request missing required fields") return jsonify({"error": "Email and new password are required"}), 400 try: updated = update_user_password(data['email'], data['new_password']) if updated: return jsonify({"success": True, "message": "Password reset successfully"}), 200 logger.error(f"Failed to update password for {data['email']}") return jsonify({"error": "Failed to update password"}), 500 except Exception as e: logger.error(f"Error in reset_password for {data['email']}: {str(e)}") return jsonify({"error": str(e)}), 500