File size: 5,445 Bytes
45a58f3
df98da1
 
 
6044c8f
df98da1
 
 
 
b9c7033
 
 
 
 
 
 
 
df98da1
 
6044c8f
 
 
 
 
 
 
 
 
45a58f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b9c7033
 
 
 
 
 
dc77965
b9c7033
 
 
6044c8f
b9c7033
 
 
 
 
 
 
675b2c8
b9c7033
 
 
06cb529
c701390
 
675b2c8
06cb529
675b2c8
6044c8f
06cb529
675b2c8
45a58f3
675b2c8
 
 
06cb529
675b2c8
06cb529
 
 
189dd8c
df98da1
 
 
 
 
6044c8f
df98da1
6044c8f
df98da1
 
 
45a58f3
df98da1
 
 
b9c7033
45a58f3
df98da1
 
 
 
 
 
 
b9c7033
45a58f3
b9c7033
 
df98da1
675b2c8
45a58f3
 
675b2c8
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""Email: Mailjet (HTTPS) β†’ Resend β†’ Brevo SMTP (port 2525) β†’ SMTP fallback."""

import os
import smtplib
import base64
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText


def _build_body(username: str, reset_url: str) -> str:
    return (
        f"Hi {username},\n\n"
        f"We received a request to reset your YapStation password.\n\n"
        f"Open this link (valid for 1 hour):\n{reset_url}\n\n"
        f"If you did not request this, ignore this email.\n\n"
        f"β€” YapStation"
    )


def _build_msg(mail_from: str, to_email: str, username: str, reset_url: str) -> MIMEMultipart:
    msg = MIMEMultipart()
    msg["Subject"] = "YapStation β€” reset your password"
    msg["From"] = mail_from
    msg["To"] = to_email
    msg.attach(MIMEText(_build_body(username, reset_url), "plain", "utf-8"))
    return msg


def _send_via_mailjet(to_email: str, username: str, reset_url: str) -> bool:
    api_key = os.environ.get("MAILJET_API_KEY", "").strip()
    secret_key = os.environ.get("MAILJET_SECRET_KEY", "").strip()
    if not api_key or not secret_key:
        return False
    try:
        import httpx
        mail_from = os.environ.get("MAIL_FROM", "yapstation.official@gmail.com").strip()
        resp = httpx.post(
            "https://api.mailjet.com/v3.1/send",
            auth=(api_key, secret_key),
            json={
                "Messages": [{
                    "From": {"Email": mail_from, "Name": "YapStation"},
                    "To": [{"Email": to_email}],
                    "Subject": "YapStation β€” reset your password",
                    "TextPart": _build_body(username, reset_url),
                }]
            },
            timeout=15,
        )
        if resp.status_code == 200:
            return True
        print(f"[mail] Mailjet error {resp.status_code}: {resp.text}")
        return False
    except Exception as e:
        print(f"[mail] Mailjet failed: {e}")
        return False


def _send_via_resend(to_email: str, username: str, reset_url: str) -> bool:
    api_key = os.environ.get("RESEND_API_KEY", "").strip()
    if not api_key:
        return False
    try:
        import httpx
        mail_from = os.environ.get("MAIL_FROM", "YapStation <onboarding@resend.dev>").strip()
        resp = httpx.post(
            "https://api.resend.com/emails",
            headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
            json={"from": mail_from, "to": [to_email], "subject": "YapStation β€” reset your password", "text": _build_body(username, reset_url)},
            timeout=15,
        )
        if resp.status_code in (200, 201):
            return True
        print(f"[mail] Resend error {resp.status_code}: {resp.text}")
        return False
    except Exception as e:
        print(f"[mail] Resend failed: {e}")
        return False


def _send_via_brevo(to_email: str, username: str, reset_url: str) -> bool:
    smtp_user = os.environ.get("BREVO_SMTP_USER", "").strip().strip('"').strip("'")
    smtp_key = os.environ.get("BREVO_API_KEY", "").strip().strip('"').strip("'").replace(" ", "")
    if not smtp_user or not smtp_key:
        return False
    mail_from = os.environ.get("MAIL_FROM", smtp_user).strip()
    msg = _build_msg(mail_from, to_email, username, reset_url)
    try:
        with smtplib.SMTP("smtp-relay.brevo.com", 2525, timeout=30) as smtp:
            smtp.ehlo(); smtp.starttls(); smtp.ehlo()
            smtp.login(smtp_user, smtp_key)
            smtp.sendmail(mail_from, [to_email], msg.as_string())
        return True
    except Exception as e:
        print(f"[mail] Brevo SMTP failed ({type(e).__name__}): {e}")
        return False


def _send_via_smtp(to_email: str, username: str, reset_url: str) -> bool:
    host = os.environ.get("SMTP_HOST", "").strip()
    if not host or not to_email:
        return False
    port = int(os.environ.get("SMTP_PORT", "587"))
    user = os.environ.get("SMTP_USER", "").strip()
    password = "".join(os.environ.get("SMTP_PASSWORD", "").split()).strip('"').strip("'")
    mail_from = os.environ.get("MAIL_FROM", user or "noreply@localhost").strip()
    msg = _build_msg(mail_from, to_email, username, reset_url)
    try:
        if port == 465:
            with smtplib.SMTP_SSL(host, port, timeout=30) as smtp:
                if user and password: smtp.login(user, password)
                smtp.sendmail(mail_from, [to_email], msg.as_string())
        else:
            with smtplib.SMTP(host, port, timeout=30) as smtp:
                smtp.ehlo(); smtp.starttls(); smtp.ehlo()
                if user and password: smtp.login(user, password)
                smtp.sendmail(mail_from, [to_email], msg.as_string())
        return True
    except Exception as e:
        print(f"[mail] SMTP send failed ({type(e).__name__}): {e}")
        return False


def send_password_reset_email(to_email: str, username: str, reset_url: str) -> bool:
    return _send_via_mailjet(to_email, username, reset_url) or _send_via_resend(to_email, username, reset_url) or _send_via_brevo(to_email, username, reset_url) or _send_via_smtp(to_email, username, reset_url)


def smtp_configured() -> bool:
    return bool(
        os.environ.get("MAILJET_API_KEY", "").strip()
        or os.environ.get("RESEND_API_KEY", "").strip()
        or os.environ.get("BREVO_SMTP_USER", "").strip()
        or os.environ.get("SMTP_HOST", "").strip()
    )