99from flask_limiter import Limiter
1010from flask_limiter .util import get_remote_address
1111
12- from sendgrid import SendGridAPIClient
13- from sendgrid . helpers . mail import Mail , Attachment , FileContent , FileName , FileType , Disposition
12+ import boto3
13+ from botocore . exceptions import ClientError
1414
1515from dotenv import load_dotenv
1616
@@ -74,32 +74,42 @@ def get_identifier(recipient, now=None, randint=None):
7474
7575def create_email (to_email , identifier , text , all_attachments , reference = '' ):
7676 """
77- Creates an email message with attachments.
77+ Creates an email message with attachments for AWS SES .
7878 """
79+ from email .mime .multipart import MIMEMultipart
80+ from email .mime .text import MIMEText
81+ from email .mime .application import MIMEApplication
82+
7983 plain_text = text .replace ('<br />' , '\n ' )
8084 subject = f'Secure Form Submission { identifier } '
8185 if reference :
8286 subject = f'{ reference } { subject } '
8387
84- message = Mail (
85- from_email = FROMEMAIL ,
86- to_emails = to_email ,
87- subject = subject ,
88- plain_text_content = plain_text )
89-
88+ # Create message container
89+ msg = MIMEMultipart ()
90+ msg ['Subject' ] = subject
91+ msg ['From' ] = FROMEMAIL
92+ msg ['To' ] = to_email
93+
94+ # Add body to email
95+ body = MIMEText (plain_text , 'plain' )
96+ msg .attach (body )
97+
98+ # Add attachments
9099 for item in all_attachments :
91100 filename = item ['filename' ]
92- attachment = item ['attachment' ]
93-
94- encoded_file = base64 . b64encode ( attachment . encode ( "utf-8" )). decode ()
95- attachedFile = Attachment (
96- FileContent ( encoded_file ),
97- FileName ( filename + '.pgp' ) ,
98- FileType ( 'application/pgp-encrypted' ) ,
99- Disposition ( 'attachment' )
101+ attachment_content = item ['attachment' ]
102+
103+ # Create attachment
104+ part = MIMEApplication ( attachment_content . encode ( 'utf-8' ))
105+ part . add_header (
106+ 'Content-Disposition' ,
107+ 'attachment' ,
108+ filename = f' { filename } .pgp'
100109 )
101- message .add_attachment (attachedFile )
102- return message
110+ msg .attach (part )
111+
112+ return msg
103113
104114def validate_recaptcha (recaptcha_response ):
105115 """
@@ -125,17 +135,39 @@ def validate_recaptcha(recaptcha_response):
125135
126136def send_email (message ):
127137 """
128- Sends the email using SendGrid and logs detailed information for debugging.
138+ Sends the email using AWS SES and logs detailed information for debugging.
129139 """
130140 try :
131- sg = SendGridAPIClient (SENDGRIDAPIKEY )
132- response = sg .send (message )
133- logging .info ('SendGrid response status code: %s' , response .status_code )
134- if response .status_code not in [200 , 201 , 202 ]:
135- logging .error ('SendGrid failed with status code: %s, response body: %s' , response .status_code , response .body )
136- raise ValueError (f"Error: Failed to send email. Status code: { response .status_code } , body: { response .body } " )
141+ # Send the email
142+ response = ses_client .send_raw_email (
143+ Source = message ['From' ],
144+ Destinations = [message ['To' ]],
145+ RawMessage = {
146+ 'Data' : message .as_string ()
147+ }
148+ )
149+
150+ # Log the response
151+ message_id = response ['MessageId' ]
152+ logging .info ('AWS SES email sent successfully. MessageId: %s' , message_id )
153+
154+ except ClientError as e :
155+ error_code = e .response ['Error' ]['Code' ]
156+ error_message = e .response ['Error' ]['Message' ]
157+ logging .error ('AWS SES error: Code=%s, Message=%s' , error_code , error_message )
158+
159+ # Provide user-friendly error messages
160+ if error_code == 'MessageRejected' :
161+ raise ValueError ('Error: Email was rejected by AWS SES. Please check the email configuration.' )
162+ elif error_code == 'MailFromDomainNotVerified' :
163+ raise ValueError ('Error: The sender email domain is not verified in AWS SES.' )
164+ elif error_code == 'ConfigurationSetDoesNotExist' :
165+ raise ValueError ('Error: AWS SES configuration error.' )
166+ else :
167+ raise ValueError (f'Error: Failed to send email. { error_message } ' )
168+
137169 except Exception as e :
138- logging .error ('Error sending email via SendGrid : %s' , str (e ))
170+ logging .error ('Error sending email via AWS SES : %s' , str (e ))
139171 raise
140172
141173
@@ -155,13 +187,23 @@ def get_forwarded_address():
155187 return get_remote_address ()
156188
157189# Validate required environment variables
158- required_env_vars = ['RECAPTCHASITEKEY' , 'RECAPTCHASECRETKEY' , 'SENDGRIDAPIKEY ' , 'SENDGRIDFROMEMAIL ' ]
190+ required_env_vars = ['RECAPTCHASITEKEY' , 'RECAPTCHASECRETKEY' , 'AWS_ACCESS_KEY_ID ' , 'AWS_SECRET_ACCESS_KEY' , 'AWS_REGION' , 'SES_FROM_EMAIL ' ]
159191validate_env_vars (required_env_vars )
160192
161193RECAPTCHASITEKEY = os .environ ['RECAPTCHASITEKEY' ]
162194RECAPTCHASECRETKEY = os .environ ['RECAPTCHASECRETKEY' ]
163- SENDGRIDAPIKEY = os .environ ['SENDGRIDAPIKEY' ]
164- FROMEMAIL = os .environ ['SENDGRIDFROMEMAIL' ]
195+ AWS_ACCESS_KEY_ID = os .environ ['AWS_ACCESS_KEY_ID' ]
196+ AWS_SECRET_ACCESS_KEY = os .environ ['AWS_SECRET_ACCESS_KEY' ]
197+ AWS_REGION = os .environ ['AWS_REGION' ]
198+ FROMEMAIL = os .environ ['SES_FROM_EMAIL' ]
199+
200+ # Initialize AWS SES client
201+ ses_client = boto3 .client (
202+ 'ses' ,
203+ region_name = AWS_REGION ,
204+ aws_access_key_id = AWS_ACCESS_KEY_ID ,
205+ aws_secret_access_key = AWS_SECRET_ACCESS_KEY
206+ )
165207
166208app = Flask (__name__ )
167209app .config .from_object (Config )
0 commit comments