import os import smtplib import logging from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from typing import Optional from sqlalchemy.ext.asyncio import AsyncSession from app.core.config import settings from app.core.i18n import locale_manager from app.services.config_service import config from app.db.session import AsyncSessionLocal logger = logging.getLogger("Email-Manager-2.0") class EmailManager: @staticmethod def _get_html_template(template_key: str, variables: dict, lang: str = "hu") -> str: """HTML sablon generálása a fordítási fájlok alapján.""" greeting = locale_manager.get(f"email.{template_key}_greeting", lang=lang, **variables) body = locale_manager.get(f"email.{template_key}_body", lang=lang, **variables) button_text = locale_manager.get(f"email.{template_key}_button", lang=lang) footer = locale_manager.get(f"email.{template_key}_footer", lang=lang) link_fallback_text = locale_manager.get("email.link_fallback", lang=lang) return f"""

{greeting}

{body}

{button_text}

{link_fallback_text}
{variables.get('link')}


{footer}

""" @staticmethod async def send_email(recipient: str, template_key: str, variables: dict, lang: str = "hu", db: Optional[AsyncSession] = None): """ E-mail küldése közvetlenül a privát SMTP szerveren keresztül. """ session_internal = False if db is None: db = AsyncSessionLocal() session_internal = True try: # Check if emails are disabled via DB config provider = await config.get_setting(db, "email_provider", default="smtp") if provider == "disabled": logger.info(f"Email küldés letiltva (Admin config). Cél: {recipient}") return html = EmailManager._get_html_template(template_key, variables, lang) subject = locale_manager.get(f"email.{template_key}_subject", lang=lang) smtp_host = os.getenv("SMTP_HOST", "mail.servicefinder.hu") smtp_port = int(os.getenv("SMTP_PORT", "465")) smtp_user = os.getenv("SMTP_USER", "noreply@servicefinder.hu") smtp_pass = os.getenv("SMTP_PASSWORD", "") from_email = os.getenv("MAIL_FROM", "noreply@servicefinder.hu") from_name = os.getenv("MAIL_FROM_NAME", "ServiceFinder") smtp_cfg = { "host": smtp_host, "port": smtp_port, "user": smtp_user, "pass": smtp_pass } logger.info(f"Using SMTP config: host={smtp_cfg['host']}, port={smtp_cfg['port']}, user={smtp_cfg['user']}") return await EmailManager._send_via_smtp(smtp_cfg, from_email, from_name, recipient, subject, html) finally: if session_internal: await db.close() @staticmethod async def _send_via_smtp(cfg: dict, from_email: str, from_name: str, recipient: str, subject: str, html: str): # Mock mode check: If APP_ENV=test or domain is example.com, skip SMTP and return success app_env = os.getenv("APP_ENV", "").lower() is_example_domain = recipient.endswith("@example.com") or "@example.com" in recipient if app_env == "test" or is_example_domain: logger.info(f"Mock mode: Skipping SMTP for {recipient} (APP_ENV={app_env}, is_example_domain={is_example_domain})") return {"status": "success", "provider": "mock", "message": "Email skipped in test mode"} try: msg = MIMEMultipart() msg["From"] = f"{from_name} <{from_email}>" msg["To"] = recipient msg["Subject"] = subject msg.attach(MIMEText(html, "html")) # Port 465 uses SMTP_SSL directly instead of STARTTLS if cfg["port"] == 465: logger.info(f"Connecting via SMTP_SSL to {cfg['host']}:{cfg['port']}") with smtplib.SMTP_SSL(cfg["host"], cfg["port"], timeout=15) as server: user = cfg.get("user", "") passwd = cfg.get("pass", "") if user and passwd: server.login(user, passwd) server.send_message(msg) else: logger.info(f"Connecting via SMTP to {cfg['host']}:{cfg['port']}") with smtplib.SMTP(cfg["host"], cfg["port"], timeout=15) as server: # Explicit STARTTLS if not 465, though we expect 465 server.starttls() user = cfg.get("user", "") passwd = cfg.get("pass", "") if user and passwd: server.login(user, passwd) server.send_message(msg) logger.info(f"SMTP siker -> {recipient}") return {"status": "success", "provider": "smtp"} except Exception as e: logger.error(f"SMTP hiba: {str(e)}") return {"status": "error", "message": str(e)} email_manager = EmailManager()