- Convert mail messaging from SMTP to Scaleway TEM mails

This commit is contained in:
Josako
2025-05-10 10:49:15 +02:00
parent a421977918
commit 12a53ebc1c
14 changed files with 90 additions and 71 deletions

View File

@@ -6,7 +6,7 @@ from flask_security.signals import user_authenticated
from werkzeug.middleware.proxy_fix import ProxyFix
import logging.config
from common.extensions import (db, migrate, bootstrap, security, mail, login_manager, cors, csrf, session,
from common.extensions import (db, migrate, bootstrap, security, login_manager, cors, csrf, session,
minio_client, simple_encryption, metrics, cache_manager)
from common.models.user import User, Role, Tenant, TenantDomain
import common.models.interaction
@@ -19,7 +19,7 @@ from .errors import register_error_handlers
from common.utils.celery_utils import make_celery, init_celery
from common.utils.template_filters import register_filters
from config.config import get_config
from eveai_app.views.security_forms import ResetPasswordForm
from eveai_app.views.security_forms import ResetPasswordForm, ForgotPasswordForm
def create_app(config_file=None):
@@ -40,6 +40,7 @@ def create_app(config_file=None):
app.config['SESSION_KEY_PREFIX'] = 'eveai_app_'
app.config['SECURITY_RESET_PASSWORD_FORM'] = ResetPasswordForm
app.config['SECURITY_FORGOT_PASSWORD_FORM'] = ForgotPasswordForm
try:
os.makedirs(app.instance_path)
@@ -79,8 +80,6 @@ def create_app(config_file=None):
# Debugging settings
if app.config['DEBUG'] is True:
app.logger.setLevel(logging.DEBUG)
mail_logger = logging.getLogger('flask_mailman')
mail_logger.setLevel(logging.DEBUG)
security_logger = logging.getLogger('flask_security')
security_logger.setLevel(logging.DEBUG)
sqlalchemy_logger = logging.getLogger('sqlalchemy.engine')
@@ -117,7 +116,6 @@ def register_extensions(app):
db.init_app(app)
migrate.init_app(app, db)
bootstrap.init_app(app)
mail.init_app(app)
csrf.init_app(app)
login_manager.init_app(app)
cors.init_app(app)

View File

@@ -38,21 +38,21 @@
<div class="tab-content tab-space">
<!-- Storage Tab -->
<div class="tab-pane fade show active" id="storage-tab" role="tabpanel">
{% set storage_fields = ['max_storage_mb', 'additional_storage_price', 'additional_storage_bucket'] %}
{% set storage_fields = ['max_storage_mb', 'additional_storage_allowed', 'additional_storage_price', 'additional_storage_bucket'] %}
{% for field in form %}
{{ render_included_field(field, readonly_fields=ext_readonly_fields, include_fields=storage_fields) }}
{% endfor %}
</div>
<!-- Embedding Tab -->
<div class="tab-pane fade" id="embedding-tab" role="tabpanel">
{% set embedding_fields = ['included_embedding_mb', 'additional_embedding_price', 'additional_embedding_bucket', 'overage_embedding'] %}
{% set embedding_fields = ['included_embedding_mb', 'additional_embedding_allowed', 'additional_embedding_price', 'additional_embedding_bucket', 'overage_embedding'] %}
{% for field in form %}
{{ render_included_field(field, readonly_fields=ext_readonly_fields, include_fields=embedding_fields) }}
{% endfor %}
</div>
<!-- Interaction Tab -->
<div class="tab-pane fade" id="interaction-tab" role="tabpanel">
{% set interaction_fields = ['included_interaction_tokens', 'additional_interaction_token_price', 'additional_interaction_bucket', 'overage_interaction'] %}
{% set interaction_fields = ['included_interaction_tokens', 'additional_interaction_allowed', 'additional_interaction_token_price', 'additional_interaction_bucket', 'overage_interaction'] %}
{% for field in form %}
{{ render_included_field(field, readonly_fields=ext_readonly_fields, include_fields=interaction_fields) }}
{% endfor %}

View File

@@ -7,7 +7,7 @@
{% block content %}
{% include "security/_messages.html" %}
<form action="{{ url_for_security('forgot_password') }}" method="post" name="forgot_password_form">
<form action="{{ url_for('security_bp.forgot_password') }}" method="post" novalidate>
{{ forgot_password_form.hidden_tag() }}
<p>

View File

@@ -26,8 +26,7 @@
{# </div>#}
{% endblock %}
{% block content_footer %}
First time here? Forgot your password?
<a href="/reset" class="text-success text-gradient font-weight-bold">Request new password</a>
Forgot your password? Contact your administrator to send you a password reset link.
{% endblock %}
{#{{ url_for_security('reset_password', token=reset_password_token) }}#}

View File

@@ -51,10 +51,12 @@ class LicenseForm(FlaskForm):
yearly_payment = BooleanField('Yearly Payment', default=False)
basic_fee = FloatField('Basic Fee', validators=[InputRequired(), NumberRange(min=0)])
max_storage_mb = IntegerField('Max Storage (MiB)', validators=[DataRequired(), NumberRange(min=1)])
additional_storage_allowed = BooleanField('Additional Storage Allowed', default=True)
additional_storage_price = FloatField('Additional Storage Token Fee',
validators=[InputRequired(), NumberRange(min=0)])
additional_storage_bucket = IntegerField('Additional Storage Bucket Size (MiB)',
validators=[DataRequired(), NumberRange(min=1)])
additional_embedding_allowed = BooleanField('Additional Embedding Allowed', default=True)
included_embedding_mb = IntegerField('Included Embedding Tokens (MiB)',
validators=[DataRequired(), NumberRange(min=1)])
additional_embedding_price = FloatField('Additional Embedding Token Fee',
@@ -63,6 +65,7 @@ class LicenseForm(FlaskForm):
validators=[DataRequired(), NumberRange(min=1)])
included_interaction_tokens = IntegerField('Included Interaction Tokens (M Tokens)',
validators=[DataRequired(), NumberRange(min=1)])
additional_interaction_allowed = BooleanField('Additional Interaction Allowed', default=True)
additional_interaction_token_price = FloatField('Additional Interaction Token Fee',
validators=[InputRequired(), NumberRange(min=0)])
additional_interaction_bucket = IntegerField('Additional Interaction Bucket Size (M Tokens)',

View File

@@ -152,8 +152,9 @@ def create_license(license_tier_id):
tenant_id = session.get('tenant').get('id')
currency = session.get('tenant').get('currency')
if current_user_has_role("Partner Admin"): # The Partner Admin can only set the end date
readonly_fields = [field.name for field in form if (field.name != 'end_date' and field.name != 'start_date')]
if current_user_has_role("Partner Admin"): # The Partner Admin can only set start & end dates, and allowed fields
readonly_fields = [field.name for field in form if (field.name != 'end_date' and field.name != 'start_date' and
not field.name.endswith('allowed'))]
if request.method == 'GET':
# Fetch the LicenseTier

View File

@@ -2,10 +2,6 @@ from flask import current_app
from flask_wtf import FlaskForm
from wtforms import PasswordField, SubmitField, StringField
from wtforms.validators import DataRequired, Length, Email, NumberRange, Optional, EqualTo
from flask_security.forms import ForgotPasswordForm
from flask_security.utils import send_mail, config_value
from common.utils.nginx_utils import prefixed_url_for
class SetPasswordForm(FlaskForm):
@@ -14,7 +10,7 @@ class SetPasswordForm(FlaskForm):
submit = SubmitField('Set Password')
class RequestResetForm(FlaskForm):
class ForgotPasswordForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
submit = SubmitField('Request Password Reset')

View File

@@ -13,7 +13,7 @@ from sqlalchemy.exc import SQLAlchemyError
from common.models.user import User
from common.utils.eveai_exceptions import EveAIException, EveAINoActiveLicense
from common.utils.nginx_utils import prefixed_url_for
from eveai_app.views.security_forms import SetPasswordForm, ResetPasswordForm, RequestResetForm
from eveai_app.views.security_forms import SetPasswordForm, ResetPasswordForm, ForgotPasswordForm
from common.extensions import db
from common.utils.security_utils import confirm_token, send_confirmation_email, send_reset_email
from common.utils.security import set_tenant_session_data, is_valid_tenant
@@ -111,16 +111,16 @@ def confirm_email(token):
return redirect(prefixed_url_for('basic_bp.confirm_email_ok'))
@security_bp.route('/reset_password_request', methods=['GET', 'POST'])
def reset_password_request():
form = RequestResetForm()
@security_bp.route('/forgot_password', methods=['GET', 'POST'])
def forgot_password():
form = ForgotPasswordForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user:
send_reset_email(user)
flash('An email with instructions to reset your password has been sent.', 'info')
return redirect(prefixed_url_for('security_bp.login'))
return render_template('security/reset_password_request.html', form=form)
return render_template('security/forgot_password.html', form=form)
@security_bp.route('/reset_password/<token>', methods=['GET', 'POST'])

View File

@@ -1,7 +1,6 @@
import uuid
from datetime import datetime as dt, timezone as tz
from flask import request, redirect, flash, render_template, Blueprint, session, current_app
from flask_mailman import EmailMessage
from flask_security import roles_accepted, current_user
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
import ast
@@ -21,6 +20,7 @@ from common.utils.eveai_exceptions import EveAIException
from common.utils.document_utils import set_logging_information, update_logging_information
from common.services.tenant_services import TenantServices
from common.services.user_services import UserServices
from common.utils.mail_utils import send_email
user_bp = Blueprint('user_bp', __name__, url_prefix='/user')
@@ -670,20 +670,15 @@ def send_api_key_notification(tenant_id, tenant_name, project_name, api_key, ser
}
try:
# Create email message
msg = EmailMessage(
msg = send_email(
subject='Your new API-key from Ask Eve AI (Evie)',
body=render_template('email/api_key_notification.html', **context),
from_email=current_app.config['MAIL_DEFAULT_SENDER'],
to=[recipient_email]
html=render_template('email/api_key_notification.html', **context),
to_email=recipient_email,
to_name=recipient_email,
)
# Set HTML content type
msg.content_subtype = "html"
# Send email
msg.send()
current_app.logger.info(f"API key notification sent to {recipient_email} for tenant {tenant_id}")
return True