Now with the an email send endpoint!
This commit is contained in:
@@ -412,6 +412,7 @@ def send_email(
|
|||||||
to_email: str,
|
to_email: str,
|
||||||
subject: str,
|
subject: str,
|
||||||
body_html: str,
|
body_html: str,
|
||||||
|
|
||||||
from_name: str = '',
|
from_name: str = '',
|
||||||
reply_to_email: str = '',
|
reply_to_email: str = '',
|
||||||
reply_to_name: str = '',
|
reply_to_name: str = '',
|
||||||
@@ -421,6 +422,8 @@ def send_email(
|
|||||||
bcc_email: str = '',
|
bcc_email: str = '',
|
||||||
bcc_name: str = '',
|
bcc_name: str = '',
|
||||||
body_text: str = '',
|
body_text: str = '',
|
||||||
|
|
||||||
|
test: bool = False
|
||||||
):
|
):
|
||||||
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
log.debug(locals())
|
log.debug(locals())
|
||||||
@@ -430,21 +433,76 @@ def send_email(
|
|||||||
message['Subject'] = subject
|
message['Subject'] = subject
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
if from_email:
|
|
||||||
|
if from_email and from_name:
|
||||||
#message['From'] = Address(display_name=from_name, username=from_email.split('@')[0], domain=from_email.split('@')[1])
|
#message['From'] = Address(display_name=from_name, username=from_email.split('@')[0], domain=from_email.split('@')[1])
|
||||||
message['From'] = Address(display_name=from_name, addr_spec=from_email)
|
try:
|
||||||
|
message['From'] = Address(display_name=from_name, addr_spec=from_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
|
elif from_email:
|
||||||
|
try:
|
||||||
|
message['From'] = Address(display_name=from_email, addr_spec=from_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
if reply_to_email:
|
|
||||||
message['Reply-To'] = Address(display_name=reply_to_name, addr_spec=reply_to_email)
|
if reply_to_email and reply_to_name:
|
||||||
if to_email:
|
try:
|
||||||
message['To'] = Address(display_name=to_name, addr_spec=to_email)
|
message['Reply-To'] = Address(display_name=reply_to_name, addr_spec=reply_to_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
|
elif reply_to_email:
|
||||||
|
try:
|
||||||
|
message['Reply-To'] = Address(display_name=reply_to_email, addr_spec=reply_to_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if to_email and to_name:
|
||||||
|
try:
|
||||||
|
message['To'] = Address(display_name=to_name, addr_spec=to_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
|
elif to_email:
|
||||||
|
try:
|
||||||
|
message['To'] = Address(display_name=to_email, addr_spec=to_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
if cc_email:
|
|
||||||
message['Cc'] = Address(display_name=cc_name, addr_spec=cc_email)
|
if cc_email and cc_name:
|
||||||
if bcc_email:
|
try:
|
||||||
message['Bcc'] = Address(display_name=bcc_name, addr_spec=bcc_email)
|
message['Cc'] = Address(display_name=cc_name, addr_spec=cc_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
|
elif cc_email:
|
||||||
|
try:
|
||||||
|
message['Cc'] = Address(display_name=cc_email, addr_spec=cc_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if bcc_email and bcc_name:
|
||||||
|
try:
|
||||||
|
message['Bcc'] = Address(display_name=bcc_name, addr_spec=bcc_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
|
elif bcc_email:
|
||||||
|
try:
|
||||||
|
message['Bcc'] = Address(display_name=bcc_email, addr_spec=bcc_email)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception('**** *** ** * ### BEGIN ### Exception Happened: Returning False * ** *** ****')
|
||||||
|
return False
|
||||||
|
|
||||||
html_version = """\
|
html_version = """\
|
||||||
<html>
|
<html>
|
||||||
@@ -479,6 +537,21 @@ def send_email(
|
|||||||
|
|
||||||
# log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
# log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
log.info('SMTP configuration, connect, and send')
|
log.info('SMTP configuration, connect, and send')
|
||||||
|
|
||||||
|
if test:
|
||||||
|
try:
|
||||||
|
with smtplib.SMTP_SSL(settings.SMTP['server'], settings.SMTP['port'], context=context) as server:
|
||||||
|
log.debug('[TEST] SMTP log in...')
|
||||||
|
server.login(settings.SMTP['username'], settings.SMTP['password'])
|
||||||
|
log.debug('[TEST] SMTP send message... [WILL NOT SEND IN TEST MODE]')
|
||||||
|
# server.send_message(message)
|
||||||
|
log.info('[TEST] Email sent!')
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
#except SMTPException:
|
||||||
|
log.error('[TEST] Error: unable to send email')
|
||||||
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with smtplib.SMTP_SSL(settings.SMTP['server'], settings.SMTP['port'], context=context) as server:
|
with smtplib.SMTP_SSL(settings.SMTP['server'], settings.SMTP['port'], context=context) as server:
|
||||||
log.debug('SMTP log in...')
|
log.debug('SMTP log in...')
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from . import config
|
|||||||
from app.log import log, logging
|
from app.log import log, logging
|
||||||
|
|
||||||
# Import the routers here first:
|
# Import the routers here first:
|
||||||
from app.routers import aether_cfg, api_crud, api, importing, sql, account, activity_log, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, data_store, event, event_abstract, event_badge, event_badge_importing, event_badge_template, event_device, event_exhibit, event_exhibit_tracking, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, fundraising, grant, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_person_group, membership_person, membership_person_profile, membership_type, membership_person_type, order, order_v3, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, qr, site, site_domain, user, websockets_redis, e_confex, e_cvent, c_idaa, e_impexium, e_stripe
|
from app.routers import aether_cfg, api_crud, api, importing, sql, account, activity_log, address, archive, archive_content, contact, cont_edu_cert, cont_edu_cert_person, data_store, event, event_abstract, event_badge, event_badge_importing, event_badge_template, event_device, event_exhibit, event_exhibit_tracking, event_file, event_importing, event_location, event_person, event_person_detail, event_person_tracking, event_presentation, event_presenter, event_registration, event_session, flask_cfg, fundraising, grant, hosted_file, journal, journal_entry, log_client_viewing, lookup, membership_cfg, membership_group, membership_person_group, membership_person, membership_person_profile, membership_type, membership_person_type, order, order_v3, order_line, order_cart, organization, page, person, person_user, post, post_comment, product, qr, site, site_domain, user, util_email, websockets_redis, e_confex, e_cvent, c_idaa, e_impexium, e_stripe
|
||||||
|
|
||||||
# from app.routers import aether_cfg, sql
|
# from app.routers import aether_cfg, sql
|
||||||
|
|
||||||
@@ -391,6 +391,10 @@ app.include_router(
|
|||||||
user.router,
|
user.router,
|
||||||
tags=['User'],
|
tags=['User'],
|
||||||
)
|
)
|
||||||
|
app.include_router(
|
||||||
|
util_email.router,
|
||||||
|
tags=['Utility: Email'],
|
||||||
|
)
|
||||||
# app.include_router(
|
# app.include_router(
|
||||||
# websockets.router,
|
# websockets.router,
|
||||||
# # prefix='/websocket',
|
# # prefix='/websocket',
|
||||||
|
|||||||
78
app/models/util_email_models.py
Normal file
78
app/models/util_email_models.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import datetime, pytz
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator
|
||||||
|
|
||||||
|
from app.db_sql import redis_lookup_id_random
|
||||||
|
from app.lib_general import log, logging
|
||||||
|
|
||||||
|
from app.models.common_field_schema import base_fields, default_num_bytes
|
||||||
|
|
||||||
|
from app.models.core_object_models import Core_Std_Obj_Base
|
||||||
|
|
||||||
|
|
||||||
|
# ### BEGIN ### API Utility: Email Models ### Email_Send_Base() ###
|
||||||
|
# Update 2023-06-27
|
||||||
|
class Email_Send_Base(BaseModel):
|
||||||
|
log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
id_random: Optional[str] = Field(
|
||||||
|
# **base_fields['email_id_random'],
|
||||||
|
alias = 'email_id_random',
|
||||||
|
)
|
||||||
|
id: Optional[int] = Field(
|
||||||
|
alias = 'email_id'
|
||||||
|
)
|
||||||
|
|
||||||
|
account_id_random: Optional[str]
|
||||||
|
account_id: Optional[int]
|
||||||
|
|
||||||
|
testing: Optional[bool] = False
|
||||||
|
|
||||||
|
from_email: str # = 'from_test@example.com'
|
||||||
|
from_name: str = None
|
||||||
|
|
||||||
|
to_email: str # = 'to_test@example.com'
|
||||||
|
to_name: str = None
|
||||||
|
|
||||||
|
cc_email: str = None
|
||||||
|
cc_name: str = None
|
||||||
|
|
||||||
|
bcc_email: str = None
|
||||||
|
bcc_name: str = None
|
||||||
|
|
||||||
|
subject: str
|
||||||
|
|
||||||
|
body_html: str
|
||||||
|
body_text: str = None
|
||||||
|
|
||||||
|
# json: Optional[str] # "json" is reserved; need to change field name? json_str?
|
||||||
|
# json_str: Optional[Union[Json, None]] = Field(
|
||||||
|
# alias = 'json',
|
||||||
|
# )
|
||||||
|
# text: Optional[str]
|
||||||
|
|
||||||
|
# notes: Optional[str]
|
||||||
|
|
||||||
|
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
|
||||||
|
|
||||||
|
@validator('id', always=True)
|
||||||
|
def email_id_lookup(cls, v, values, **kwargs):
|
||||||
|
if isinstance(v, int) and v > 0: return v
|
||||||
|
elif id_random := values.get('id_random'):
|
||||||
|
return redis_lookup_id_random(record_id_random=id_random, table_name='email')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@validator('account_id', always=True)
|
||||||
|
def account_id_lookup(cls, v, values, **kwargs):
|
||||||
|
if isinstance(v, int) and v > 0: return v
|
||||||
|
elif id_random := values.get('account_id_random'):
|
||||||
|
return redis_lookup_id_random(record_id_random=id_random, table_name='account')
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
underscore_attrs_are_private = True
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
fields = base_fields
|
||||||
|
# ### END ### API Grant Models ### Grant_Base() ###
|
||||||
77
app/routers/util_email.py
Normal file
77
app/routers/util_email.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import datetime, pytz, time
|
||||||
|
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, Response, status
|
||||||
|
from pydantic import BaseModel, EmailStr, Field
|
||||||
|
from typing import Dict, List, Optional, Set, Union
|
||||||
|
|
||||||
|
from app.lib_general import log, logging, common_route_params, Common_Route_Params, send_email
|
||||||
|
from app.config import settings
|
||||||
|
from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select, sql_delete, get_id_random, redis_lookup_id_random
|
||||||
|
|
||||||
|
from app.routers.api_crud import delete_obj_template, get_obj_template, get_obj_li_template, patch_obj_template, post_obj_template
|
||||||
|
|
||||||
|
from app.methods.data_store_methods import create_update_data_store_obj, get_data_store_rec_list, load_data_store_obj, load_data_store_obj_w_code
|
||||||
|
|
||||||
|
# from app.models.common_field_schema import default_num_bytes
|
||||||
|
from app.models.util_email_models import Email_Send_Base
|
||||||
|
from app.models.response_models import Resp_Body_Base, mk_resp
|
||||||
|
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
# ### BEGIN ### API Utility: Email ### util_email_send_obj() ###
|
||||||
|
# Updated 2023-06-27
|
||||||
|
@router.post('/util/email/send', response_model=Resp_Body_Base)
|
||||||
|
async def util_email_send_obj(
|
||||||
|
email_send_obj: Email_Send_Base,
|
||||||
|
|
||||||
|
test: bool = False,
|
||||||
|
|
||||||
|
return_obj: bool = True,
|
||||||
|
|
||||||
|
commons: Common_Route_Params = Depends(common_route_params),
|
||||||
|
):
|
||||||
|
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
|
||||||
|
log.debug(locals())
|
||||||
|
|
||||||
|
resp_data = {}
|
||||||
|
|
||||||
|
from_email = email_send_obj.from_email
|
||||||
|
resp_data['from_email'] = from_email
|
||||||
|
from_name = email_send_obj.from_name
|
||||||
|
|
||||||
|
to_email = email_send_obj.to_email
|
||||||
|
resp_data['to_email'] = to_email
|
||||||
|
to_name = email_send_obj.to_name
|
||||||
|
|
||||||
|
bcc_email = email_send_obj.bcc_email
|
||||||
|
bcc_name = email_send_obj.bcc_name
|
||||||
|
|
||||||
|
subject = email_send_obj.subject
|
||||||
|
resp_data['subject'] = subject[:20]
|
||||||
|
|
||||||
|
body_html = email_send_obj.body_html
|
||||||
|
body_text = email_send_obj.body_text
|
||||||
|
|
||||||
|
log.info('Trying send_email()...')
|
||||||
|
|
||||||
|
# NOTE: This all works, but I think it could be done better???
|
||||||
|
|
||||||
|
if send_email(from_email=from_email, from_name=from_name, to_email=to_email, to_name=to_name, bcc_email=bcc_email, bcc_name=bcc_name, subject=subject, body_text=body_text, body_html=body_html, test=test):
|
||||||
|
status_code = 200
|
||||||
|
status_message = f'Email was sent to <{to_email}>.'
|
||||||
|
else:
|
||||||
|
status_code = 400
|
||||||
|
status_message = f'Email failed to send to <{to_email}>.'
|
||||||
|
log.info(status_message)
|
||||||
|
|
||||||
|
if return_obj:
|
||||||
|
return mk_resp(data=resp_data, status_code=status_code, response=commons.response, status_message=status_message)
|
||||||
|
else:
|
||||||
|
if status_code == 200:
|
||||||
|
resp_data = True
|
||||||
|
else:
|
||||||
|
resp_data = False
|
||||||
|
|
||||||
|
return mk_resp(data=resp_data, status_code=status_code, response=commons.response, status_message=status_message)
|
||||||
|
# ### END ### API Utility: Email ### util_email_send_obj() ###
|
||||||
Reference in New Issue
Block a user