3232LOCAL_UPLOAD_DIR = Path (os .getenv ("LOCAL_UPLOAD_DIR" , "./uploads" ))
3333ONESHOT_PUBLIC_DOMAIN = os .getenv ("ONESHOT_PUBLIC_DOMAIN" , "localhost:5173" )
3434SMTP_HOST = os .getenv ("ONESHOT_SMTP_HOST" , "" )
35- SMTP_PORT = int (os .getenv ("ONESHOT_SMTP_PORT" , "587" ))
3635SMTP_USERNAME = os .getenv ("ONESHOT_SMTP_USERNAME" , "" )
3736SMTP_PASSWORD = os .getenv ("ONESHOT_SMTP_PASSWORD" , "" )
3837SMTP_FROM = os .getenv ("ONESHOT_SMTP_FROM" , "no-reply@oneshot.local" )
38+ SMTP_USE_SSL = os .getenv ("ONESHOT_SMTP_USE_SSL" , "false" ).lower () in {
39+ "1" ,
40+ "true" ,
41+ "yes" ,
42+ }
43+ SMTP_TIMEOUT_SECONDS = float (os .getenv ("ONESHOT_SMTP_TIMEOUT" , "10" ))
3944
4045router = APIRouter ()
4146logger = logging .getLogger (__name__ )
4247
4348
49+ def _smtp_port () -> int :
50+ raw = os .getenv ("ONESHOT_SMTP_PORT" , "587" )
51+ try :
52+ return int (raw )
53+ except ValueError :
54+ logger .warning ("Invalid ONESHOT_SMTP_PORT=%r; falling back to 587" , raw )
55+ return 587
56+
57+
4458class CreateOneShotTokenRequest (BaseModel ):
4559 target_email : str | None = None
4660
@@ -73,10 +87,10 @@ def _oneshot_link(token_id: str) -> str:
7387 return f"https://{ ONESHOT_PUBLIC_DOMAIN } /oneshot#token={ token_id } "
7488
7589
76- async def _send_oneshot_email (target_email : str , link : str ) -> None :
90+ async def _send_oneshot_email (target_email : str , link : str ) -> bool :
7791 if not SMTP_HOST :
7892 logger .warning ("OneShot email dispatch skipped: ONESHOT_SMTP_HOST is not configured" )
79- return
93+ return False
8094
8195 message = EmailMessage ()
8296 message ["Subject" ] = "Secure OneShot Upload Link"
@@ -92,13 +106,33 @@ async def _send_oneshot_email(target_email: str, link: str) -> None:
92106 )
93107
94108 try :
95- with smtplib .SMTP (SMTP_HOST , SMTP_PORT , timeout = 10 ) as smtp :
96- smtp .starttls ()
109+ if SMTP_USE_SSL :
110+ smtp_ctx = smtplib .SMTP_SSL (
111+ SMTP_HOST ,
112+ _smtp_port (),
113+ timeout = SMTP_TIMEOUT_SECONDS ,
114+ )
115+ else :
116+ smtp_ctx = smtplib .SMTP (
117+ SMTP_HOST ,
118+ _smtp_port (),
119+ timeout = SMTP_TIMEOUT_SECONDS ,
120+ )
121+
122+ with smtp_ctx as smtp :
123+ if not SMTP_USE_SSL :
124+ code , _message = smtp .starttls ()
125+ if code not in {220 , 250 }:
126+ raise smtplib .SMTPException (
127+ f"STARTTLS handshake failed with SMTP code { code } "
128+ )
97129 if SMTP_USERNAME and SMTP_PASSWORD :
98130 smtp .login (SMTP_USERNAME , SMTP_PASSWORD )
99131 smtp .send_message (message )
132+ return True
100133 except Exception :
101134 logger .exception ("Failed to dispatch OneShot email to %s" , target_email )
135+ return False
102136
103137
104138@router .post (
0 commit comments