From 78dd6fec6eb4838a413b5dfc45a14b5c29380ed2 Mon Sep 17 00:00:00 2001 From: "CHIEFSOFT\\ameye" Date: Fri, 5 Sep 2025 19:29:28 -0400 Subject: [PATCH] improve email --- app/api/enums/settings_items_data.py | 3 +- app/api/routes/routes.py | 7 +- app/api/services/account.py | 125 ++++++++++++++++++++++++--- app/api/services/base_service.py | 86 ++++++++++++++++++ app/api/services/register.py | 3 +- 5 files changed, 208 insertions(+), 16 deletions(-) diff --git a/app/api/enums/settings_items_data.py b/app/api/enums/settings_items_data.py index 382be72..3fcbbd2 100644 --- a/app/api/enums/settings_items_data.py +++ b/app/api/enums/settings_items_data.py @@ -8,7 +8,8 @@ class SettingsItemsData: data = { "site_title": {"name": 'Title', "controls": 'TEXT', "active": True, "list_order": 0}, "site_description": {"name": 'Description', "controls": 'TEXTAREA', "active": True, "list_order": 1}, - "site_logo_text": {"name": 'Logo Text', "controls": 'TEXT', "active": True, "list_order": 2}, + "site_logo_text": {"name": 'Logo Text', "controls": 'TEXT', "active": True, "list_order": 2, + "extra": {"maxlength": 25}}, "site_contact_email": {"name": 'Email', "controls": 'TEXT', "active": True, "list_order": 3}, "site_contact_phone": {"name": 'Phone', "controls": 'TEXT', "active": True, "list_order": 4}, } diff --git a/app/api/routes/routes.py b/app/api/routes/routes.py index 9dae293..99b6932 100644 --- a/app/api/routes/routes.py +++ b/app/api/routes/routes.py @@ -453,7 +453,12 @@ def common_practice(): # return jsonify(result) # =================================================== - +# Health Check Endpoint +@api.route("/testemail", methods=["GET"]) +def email_check(): + data ={} + AccountService.process_test_email(data) + return {"status": "ok"}, 200 # Health Check Endpoint @api.route("/health", methods=["GET"]) diff --git a/app/api/services/account.py b/app/api/services/account.py index 17cca0b..fd77c97 100644 --- a/app/api/services/account.py +++ b/app/api/services/account.py @@ -14,6 +14,11 @@ from werkzeug.security import generate_password_hash, check_password_hash from app.api.schemas.user import UserSchema from app.api.schemas.start_profile import StartProfileSchema +from flask_mail import Mail, Message +import smtplib +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart + import datetime import jwt import random @@ -21,6 +26,11 @@ from app.config import Config class AccountService(BaseService): + JWT_SECRET_KEY = Config.JWT_SECRET_KEY + + SEND_EMAIL_FROM = Config.SEND_EMAIL_FROM + SEND_EMAIL_PASS = Config.SEND_EMAIL_PASS + THIS_SITE_URL = Config.THIS_SITE_URL @staticmethod def process_bar_request(data): @@ -51,22 +61,20 @@ class AccountService(BaseService): uid = str(validated_data.get('uid')) member_data = Members.get_member_by_uid(uid) - - option_name = member_data.option_name # "Free Trial" + option_name = member_data.option_name # "Free Trial" if option_name is not None and option_name != "": next_bill = f"Bill: {member_data.next_billing}" view_sub = "Account" - bill_style=" next_billing " + bill_style = " next_billing " else: option_name = "Free Trial" next_bill = f"Ends: {member_data.trial_end}" - view_sub = "Upgrade Account" + view_sub = "Upgrade Account" bill_style = " billing " - contacts_count =0 # random.randint(0, 10) - appointments_count =0 - site_traffic_count =0 - + contacts_count = 0 # random.randint(0, 10) + appointments_count = 0 + site_traffic_count = 0 bar_data = { "last_update": datetime.datetime.utcnow(), @@ -74,9 +82,11 @@ class AccountService(BaseService): {"id": "1", "description": "Contacts", "last_update": "10-10-2010 11:00 AM", "value": contacts_count, "data_span": 'Last 2 months', "link": "/contacts", "extra_style": ''}, {"id": "2", "description": "Appointments", "last_update": "10-12-2010 11:30 AM", - "value": appointments_count, "data_span": 'Last 14 days', "link": "/appointments", "extra_style": ''}, + "value": appointments_count, "data_span": 'Last 14 days', "link": "/appointments", + "extra_style": ''}, {"id": "3", "description": "Site Traffic", "last_update": "10-10-2010 11:30 AM", - "value": site_traffic_count, "data_span": 'Past 12 hours', "link": "/reports/traffic", "extra_style": ''}, + "value": site_traffic_count, "data_span": 'Past 12 hours', "link": "/reports/traffic", + "extra_style": ''}, {"id": "4", "description": view_sub, "last_update": "10-12-2010 11:30 AM", "value": option_name, "data_span": next_bill, "link": "/subscription", "extra_style": bill_style} @@ -322,7 +332,6 @@ class AccountService(BaseService): db.session.rollback() return ResponseHelper.internal_server_error() - @staticmethod def process_payments_data(data): @@ -345,7 +354,7 @@ class AccountService(BaseService): 'option_name': t.option_name, 'option_type': t.option_type, 'payment_uid': t.payment_uid, - 'amount': round( t.amount*0.01, 2), + 'amount': round(t.amount * 0.01, 2), 'status': t.status, 'added': t.added }) @@ -373,4 +382,94 @@ class AccountService(BaseService): except Exception as e: logger.error(f"An error occurred: {str(e)}", exc_info=True) db.session.rollback() - return ResponseHelper.internal_server_error() \ No newline at end of file + return ResponseHelper.internal_server_error() + + @staticmethod + def process_test_email(data): + logger.info(f"Email Test Enter", exc_info=True) + AccountService.send_register_mail('ameye@chiefsoft.com', 'pend-uid---', 100, 'olutest', 'Ameyetest') + logger.info(f"Email Test", exc_info=True) + + def send_register_mail(signup_email, pending_uid, pending_id, firstname, lastname): + + pending_member = { + "email": signup_email, + "pending_uid": pending_uid, + "first_name": firstname, + "last_name": lastname, + "pending_id": pending_id, + } + jwt_part = jwt.encode( + {"user": pending_member, 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=3330)}, + AccountService.JWT_SECRET_KEY, algorithm='HS256' + ) + panel_url = panel_url = AccountService.THIS_SITE_URL # "https://qa-panel.mermsemr.com" + link_url = str(panel_url) + '/csignup/' + jwt_part + + msg_body = f""" + Hello {firstname}, + + You received this message for account verification + + Follow the link:{link_url} + + For any Support + Reach Out + support@mermsemr.com + """ + + sender_email = AccountService.SEND_EMAIL_FROM + sender_password = AccountService.SEND_EMAIL_PASS + receiver_email = signup_email + subject = "Verify your MERMS(AI) Account Setup" + body = msg_body + + html_body = f"""\ + + + + + + + + + + + + + + + + +
+ +
+ Hello {firstname}! +
+ You have received this message for your account verification
+
+ Follow the link: link to complete the verification process.
+
+ + + """ + + msg = MIMEMultipart() + msg['Subject'] = subject + msg['From'] = sender_email + msg['To'] = receiver_email + msg.attach(MIMEText(html_body, 'html')) # or 'html' for HTML content + + try: + # For Gmail, use 'smtp.gmail.com' and port 587 (TLS) or 465 (SSL) + # For other providers, consult their documentation for SMTP server and port + server = smtplib.SMTP('smtp.gmail.com', 587) + # server.starttls() # Enable TLS encryption + server.login(sender_email, sender_password) + server.sendmail(sender_email, receiver_email, msg.as_string()) + print("Email sent successfully!") + except Exception as e: + print(f"Error sending email: {e}") + logger.error(f"Error sending email: {e}") + finally: + server.quit() # Close the connection diff --git a/app/api/services/base_service.py b/app/api/services/base_service.py index f24a8ae..d898a49 100644 --- a/app/api/services/base_service.py +++ b/app/api/services/base_service.py @@ -132,6 +132,92 @@ class BaseService: server.quit() # Close the connection + def send_verify_signup_mail(signup_email, pending_uid, pending_id, firstname, lastname): + + pending_member = { + "email": signup_email, + "pending_uid": pending_uid, + "first_name": firstname, + "last_name": lastname, + "pending_id": pending_id, + } + jwt_part = jwt.encode( + {"user": pending_member, 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=3330)}, + BaseService.JWT_SECRET_KEY, algorithm='HS256' + ) + panel_url = panel_url = BaseService.THIS_SITE_URL # "https://qa-panel.mermsemr.com" + link_url = str(panel_url) + '/csignup/' + jwt_part + + msg_body = f""" + Hello {firstname}, + + You received this message for account verification + + Follow the link:{link_url} + + For any Support + Reach Out + support@mermsemr.com + """ + + sender_email = BaseService.SEND_EMAIL_FROM + sender_password = BaseService.SEND_EMAIL_PASS + receiver_email = signup_email + subject = "Verify your MERMS(AI) Account Setup" + body = msg_body + + html_body = f"""\ + + + + + + + + + + + + + + + + +
+ +
+ Hello {firstname}! +
+ You have received this message for your account verification
+
+ Follow the link: link to complete the verification process.
+
+ + + """ + + msg = MIMEMultipart() + msg['Subject'] = subject + msg['From'] = sender_email + msg['To'] = receiver_email + msg.attach(MIMEText(html_body, 'html')) # or 'html' for HTML content + + try: + # For Gmail, use 'smtp.gmail.com' and port 587 (TLS) or 465 (SSL) + # For other providers, consult their documentation for SMTP server and port + server = smtplib.SMTP('smtp.gmail.com', 587) + # server.starttls() # Enable TLS encryption + server.login(sender_email, sender_password) + server.sendmail(sender_email, receiver_email, msg.as_string()) + print("Email sent successfully!") + except Exception as e: + print(f"Error sending email: {e}") + logger.error(f"Error sending email: {e}") + finally: + server.quit() # Close the connection + + + @classmethod def validate_data(cls, data, schema): """ diff --git a/app/api/services/register.py b/app/api/services/register.py index 4a880c4..c30614b 100644 --- a/app/api/services/register.py +++ b/app/api/services/register.py @@ -167,7 +167,8 @@ class RegisterService(BaseService): email = validated_data.get('email') regData = MembersPending.add_members_pending( firstname, lastname, email) - send_register_mail(regData.email, regData.uid, regData.id, firstname, lastname) + BaseService.send_verify_signup_mail(regData.email, regData.uid, regData.id, firstname, lastname) + # send_register_mail(regData.email, regData.uid, regData.id, firstname, lastname) response_data = { "pending_id": regData.id,