""" ShopDB Email API ================ Flask API for sending emails via SMTP with STARTTLS support. Designed to be called from Classic ASP pages that cannot handle STARTTLS. Environment Variables: SMTP_HOST - SMTP server hostname SMTP_PORT - SMTP port (default: 587) SMTP_USER - SMTP username SMTP_PASS - SMTP password SMTP_FROM - Default from address (optional) API_KEY - Simple API key for authentication (optional) DB_HOST - MySQL host for distribution groups (default: 192.168.122.1) DB_USER - MySQL username (default: 570005354) DB_PASS - MySQL password DB_NAME - MySQL database (default: shopdb) Usage from ASP: Set http = Server.CreateObject("MSXML2.ServerXMLHTTP.6.0") http.open "POST", "http://localhost:5000/api/send", False http.setRequestHeader "Content-Type", "application/json" http.send "{""to"":""user@example.com"",""subject"":""Test"",""message"":""Hello""}" """ import os import smtplib import logging from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from flask import Flask, request, jsonify from functools import wraps # Optional MySQL support for distribution groups try: import mysql.connector MYSQL_AVAILABLE = True except ImportError: MYSQL_AVAILABLE = False app = Flask(__name__) # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # Configuration from environment SMTP_HOST = os.environ.get('SMTP_HOST', '') SMTP_PORT = int(os.environ.get('SMTP_PORT', '587')) SMTP_USER = os.environ.get('SMTP_USER', '') SMTP_PASS = os.environ.get('SMTP_PASS', '') SMTP_FROM = os.environ.get('SMTP_FROM', SMTP_USER) API_KEY = os.environ.get('API_KEY', '') # Database config for distribution groups DB_HOST = os.environ.get('DB_HOST', '192.168.122.1') DB_PORT = int(os.environ.get('DB_PORT', '3306')) DB_USER = os.environ.get('DB_USER', '570005354') DB_PASS = os.environ.get('DB_PASS', '570005354') DB_NAME = os.environ.get('DB_NAME', 'shopdb') def require_api_key(f): """Optional API key authentication decorator.""" @wraps(f) def decorated(*args, **kwargs): if API_KEY: provided_key = request.headers.get('X-API-Key') or request.args.get('api_key') if provided_key != API_KEY: return jsonify({'success': False, 'error': 'Invalid or missing API key'}), 401 return f(*args, **kwargs) return decorated def get_db_connection(): """Get MySQL database connection.""" if not MYSQL_AVAILABLE: return None try: return mysql.connector.connect( host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASS, database=DB_NAME ) except Exception as e: logger.error(f"Database connection failed: {e}") return None def send_email(to_addresses, subject, message, html=False, from_addr=None): """ Send email via SMTP with STARTTLS. Args: to_addresses: Single email or list of emails subject: Email subject message: Email body (plain text or HTML) html: If True, send as HTML email from_addr: Optional from address (defaults to SMTP_FROM) Returns: tuple: (success: bool, error_message: str or None) """ if not SMTP_HOST or not SMTP_USER or not SMTP_PASS: return False, "SMTP not configured. Set SMTP_HOST, SMTP_USER, SMTP_PASS environment variables." # Normalize to list if isinstance(to_addresses, str): to_addresses = [to_addresses] # Filter empty addresses to_addresses = [addr.strip() for addr in to_addresses if addr and addr.strip()] if not to_addresses: return False, "No valid recipient addresses provided" from_addr = from_addr or SMTP_FROM try: # Create message if html: msg = MIMEMultipart('alternative') msg.attach(MIMEText(message, 'html')) else: msg = MIMEText(message, 'plain') msg['Subject'] = subject msg['From'] = from_addr msg['To'] = ', '.join(to_addresses) # Connect and send logger.info(f"Connecting to {SMTP_HOST}:{SMTP_PORT}") with smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=30) as server: server.ehlo() server.starttls() server.ehlo() server.login(SMTP_USER, SMTP_PASS) server.sendmail(from_addr, to_addresses, msg.as_string()) logger.info(f"Email sent successfully to {to_addresses}") return True, None except smtplib.SMTPAuthenticationError as e: error = f"SMTP authentication failed: {e}" logger.error(error) return False, error except smtplib.SMTPException as e: error = f"SMTP error: {e}" logger.error(error) return False, error except Exception as e: error = f"Failed to send email: {e}" logger.error(error) return False, error @app.route('/api/health', methods=['GET']) def health_check(): """Health check endpoint.""" smtp_configured = bool(SMTP_HOST and SMTP_USER and SMTP_PASS) return jsonify({ 'status': 'ok', 'smtp_configured': smtp_configured, 'mysql_available': MYSQL_AVAILABLE }) @app.route('/api/send', methods=['POST']) @require_api_key def send_email_endpoint(): """ Send an email. Request JSON: { "to": "email@example.com" or ["email1@example.com", "email2@example.com"], "subject": "Email subject", "message": "Email body", "html": false, // optional, default false "from": "sender@example.com" // optional } Response JSON: {"success": true} or {"success": false, "error": "error message"} """ try: data = request.get_json() if not data: return jsonify({'success': False, 'error': 'No JSON data provided'}), 400 to_addresses = data.get('to') subject = data.get('subject', '') message = data.get('message', '') html = data.get('html', False) from_addr = data.get('from') if not to_addresses: return jsonify({'success': False, 'error': 'Missing "to" field'}), 400 if not subject: return jsonify({'success': False, 'error': 'Missing "subject" field'}), 400 if not message: return jsonify({'success': False, 'error': 'Missing "message" field'}), 400 success, error = send_email(to_addresses, subject, message, html, from_addr) if success: return jsonify({'success': True}) else: return jsonify({'success': False, 'error': error}), 500 except Exception as e: logger.error(f"Error in send endpoint: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/send-to-group', methods=['POST']) @require_api_key def send_to_distribution_group(): """ Send email to a distribution group from the database. Request JSON: { "group_id": 1, // or "group_name": "IT Support" "subject": "Email subject", "message": "Email body", "html": false } """ if not MYSQL_AVAILABLE: return jsonify({'success': False, 'error': 'MySQL connector not installed'}), 500 try: data = request.get_json() if not data: return jsonify({'success': False, 'error': 'No JSON data provided'}), 400 group_id = data.get('group_id') group_name = data.get('group_name') subject = data.get('subject', '') message = data.get('message', '') html = data.get('html', False) if not group_id and not group_name: return jsonify({'success': False, 'error': 'Missing group_id or group_name'}), 400 # Look up distribution group conn = get_db_connection() if not conn: return jsonify({'success': False, 'error': 'Database connection failed'}), 500 try: cursor = conn.cursor(dictionary=True) if group_id: cursor.execute( "SELECT email FROM distributiongroups WHERE distributiongroupid = %s AND isactive = 1", (group_id,) ) else: cursor.execute( "SELECT email FROM distributiongroups WHERE name = %s AND isactive = 1", (group_name,) ) result = cursor.fetchone() if not result: return jsonify({'success': False, 'error': 'Distribution group not found or inactive'}), 404 to_address = result['email'] finally: cursor.close() conn.close() success, error = send_email(to_address, subject, message, html) if success: return jsonify({'success': True, 'sent_to': to_address}) else: return jsonify({'success': False, 'error': error}), 500 except Exception as e: logger.error(f"Error in send-to-group endpoint: {e}") return jsonify({'success': False, 'error': str(e)}), 500 @app.route('/api/groups', methods=['GET']) @require_api_key def list_distribution_groups(): """List all active distribution groups.""" if not MYSQL_AVAILABLE: return jsonify({'success': False, 'error': 'MySQL connector not installed'}), 500 conn = get_db_connection() if not conn: return jsonify({'success': False, 'error': 'Database connection failed'}), 500 try: cursor = conn.cursor(dictionary=True) cursor.execute( "SELECT distributiongroupid, name, email FROM distributiongroups WHERE isactive = 1 ORDER BY name" ) groups = cursor.fetchall() return jsonify({'success': True, 'groups': groups}) except Exception as e: logger.error(f"Error listing groups: {e}") return jsonify({'success': False, 'error': str(e)}), 500 finally: cursor.close() conn.close() if __name__ == '__main__': # Check configuration if not SMTP_HOST: logger.warning("SMTP_HOST not set - email sending will fail") if not SMTP_USER: logger.warning("SMTP_USER not set - email sending will fail") if not SMTP_PASS: logger.warning("SMTP_PASS not set - email sending will fail") logger.info(f"Starting Email API on port 5000") logger.info(f"SMTP: {SMTP_HOST}:{SMTP_PORT}") app.run(host='0.0.0.0', port=5000, debug=False)