SMTP and other email realted settings
This commit is contained in:
@@ -2,15 +2,22 @@
|
||||
Email Service - Multi-provider email sending
|
||||
|
||||
Uses Resend for transactional emails with fallback to Django's send_mail.
|
||||
Also supports direct SMTP configuration.
|
||||
Supports template rendering and multiple email types.
|
||||
|
||||
Configuration stored in IntegrationProvider model (provider_id='resend')
|
||||
and EmailSettings model for SMTP and provider selection.
|
||||
"""
|
||||
import hashlib
|
||||
import hmac
|
||||
import logging
|
||||
import re
|
||||
import smtplib
|
||||
import time
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.base import MIMEBase
|
||||
from email import encoders
|
||||
from typing import Optional, List, Dict, Any
|
||||
from urllib.parse import urlencode
|
||||
from django.core.mail import send_mail
|
||||
@@ -162,9 +169,10 @@ class EmailService:
|
||||
Unified email service supporting multiple providers.
|
||||
|
||||
Primary: Resend (for production transactional emails)
|
||||
Alternative: SMTP (configurable via EmailSettings)
|
||||
Fallback: Django's send_mail (uses EMAIL_BACKEND from settings)
|
||||
|
||||
Uses EmailSettings model for configuration (from_email, from_name, etc.)
|
||||
Uses EmailSettings model for configuration (from_email, from_name, provider selection, SMTP settings)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -172,6 +180,8 @@ class EmailService:
|
||||
self._resend_config = {}
|
||||
self._brevo_configured = False
|
||||
self._brevo_config = {}
|
||||
self._smtp_configured = False
|
||||
self._smtp_config = {}
|
||||
self._email_settings = None
|
||||
self._setup_providers()
|
||||
|
||||
@@ -193,6 +203,22 @@ class EmailService:
|
||||
else:
|
||||
logger.info("Resend provider not configured in IntegrationProvider")
|
||||
|
||||
# Setup SMTP from EmailSettings
|
||||
if self._email_settings:
|
||||
smtp_host = self._email_settings.smtp_host
|
||||
if smtp_host:
|
||||
self._smtp_config = {
|
||||
'host': smtp_host,
|
||||
'port': self._email_settings.smtp_port,
|
||||
'username': self._email_settings.smtp_username,
|
||||
'password': self._email_settings.smtp_password,
|
||||
'use_tls': self._email_settings.smtp_use_tls,
|
||||
'use_ssl': self._email_settings.smtp_use_ssl,
|
||||
'timeout': self._email_settings.smtp_timeout,
|
||||
}
|
||||
self._smtp_configured = True
|
||||
logger.info(f"SMTP email provider initialized: {smtp_host}:{self._email_settings.smtp_port}")
|
||||
|
||||
# Setup Brevo (future - for marketing emails)
|
||||
brevo_provider = IntegrationProvider.get_provider('brevo')
|
||||
if brevo_provider and brevo_provider.api_key:
|
||||
@@ -200,6 +226,14 @@ class EmailService:
|
||||
self._brevo_configured = True
|
||||
logger.info("Brevo email provider initialized")
|
||||
|
||||
@property
|
||||
def email_provider(self) -> str:
|
||||
"""Get selected email provider from EmailSettings"""
|
||||
settings_obj = self._get_settings()
|
||||
if settings_obj and settings_obj.email_provider:
|
||||
return settings_obj.email_provider
|
||||
return 'resend' # Default to resend
|
||||
|
||||
def _get_settings(self):
|
||||
"""Get fresh email settings (refreshes on each call)"""
|
||||
if not self._email_settings:
|
||||
@@ -373,8 +407,32 @@ class EmailService:
|
||||
sender_email = from_email or self.from_email
|
||||
from_address = f"{sender_name} <{sender_email}>"
|
||||
|
||||
# Try Resend first
|
||||
if self._resend_configured:
|
||||
# Select email provider based on EmailSettings configuration
|
||||
selected_provider = self.email_provider
|
||||
|
||||
if selected_provider == 'smtp' and self._smtp_configured:
|
||||
return self._send_via_smtp(
|
||||
to=to,
|
||||
subject=subject,
|
||||
html=html,
|
||||
text=text,
|
||||
from_address=from_address,
|
||||
reply_to=reply_to or self.reply_to,
|
||||
attachments=attachments,
|
||||
)
|
||||
elif selected_provider == 'resend' and self._resend_configured:
|
||||
return self._send_via_resend(
|
||||
to=to,
|
||||
subject=subject,
|
||||
html=html,
|
||||
text=text,
|
||||
from_address=from_address,
|
||||
reply_to=reply_to or self.reply_to,
|
||||
attachments=attachments,
|
||||
tags=tags,
|
||||
)
|
||||
elif self._resend_configured:
|
||||
# Fallback to Resend if it's configured
|
||||
return self._send_via_resend(
|
||||
to=to,
|
||||
subject=subject,
|
||||
@@ -465,6 +523,101 @@ class EmailService:
|
||||
from_email=from_address.split('<')[-1].rstrip('>'),
|
||||
)
|
||||
|
||||
def _send_via_smtp(
|
||||
self,
|
||||
to: List[str],
|
||||
subject: str,
|
||||
html: Optional[str],
|
||||
text: Optional[str],
|
||||
from_address: str,
|
||||
reply_to: Optional[str],
|
||||
attachments: Optional[List[Dict]],
|
||||
) -> Dict[str, Any]:
|
||||
"""Send email via direct SMTP connection"""
|
||||
try:
|
||||
# Create message
|
||||
msg = MIMEMultipart('alternative')
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = from_address
|
||||
msg['To'] = ', '.join(to)
|
||||
|
||||
if reply_to:
|
||||
msg['Reply-To'] = reply_to
|
||||
|
||||
# Add custom headers
|
||||
msg['X-Mailer'] = 'IGNY8 Email Service'
|
||||
|
||||
# Attach text version
|
||||
if text:
|
||||
part1 = MIMEText(text, 'plain', 'utf-8')
|
||||
msg.attach(part1)
|
||||
|
||||
# Attach HTML version
|
||||
if html:
|
||||
part2 = MIMEText(html, 'html', 'utf-8')
|
||||
msg.attach(part2)
|
||||
|
||||
# Handle attachments
|
||||
if attachments:
|
||||
for attachment in attachments:
|
||||
part = MIMEBase('application', 'octet-stream')
|
||||
content = attachment.get('content', b'')
|
||||
if isinstance(content, str):
|
||||
content = content.encode('utf-8')
|
||||
part.set_payload(content)
|
||||
encoders.encode_base64(part)
|
||||
filename = attachment.get('filename', 'attachment')
|
||||
part.add_header('Content-Disposition', f'attachment; filename="{filename}"')
|
||||
msg.attach(part)
|
||||
|
||||
# Get SMTP configuration
|
||||
smtp_host = self._smtp_config.get('host')
|
||||
smtp_port = self._smtp_config.get('port', 587)
|
||||
smtp_username = self._smtp_config.get('username')
|
||||
smtp_password = self._smtp_config.get('password')
|
||||
use_tls = self._smtp_config.get('use_tls', True)
|
||||
use_ssl = self._smtp_config.get('use_ssl', False)
|
||||
timeout = self._smtp_config.get('timeout', 30)
|
||||
|
||||
# Connect and send
|
||||
if use_ssl:
|
||||
server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=timeout)
|
||||
else:
|
||||
server = smtplib.SMTP(smtp_host, smtp_port, timeout=timeout)
|
||||
if use_tls:
|
||||
server.starttls()
|
||||
|
||||
if smtp_username and smtp_password:
|
||||
server.login(smtp_username, smtp_password)
|
||||
|
||||
server.sendmail(
|
||||
from_address.split('<')[-1].rstrip('>'),
|
||||
to,
|
||||
msg.as_string()
|
||||
)
|
||||
server.quit()
|
||||
|
||||
logger.info(f"Email sent via SMTP: {subject} to {to}")
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'id': None, # SMTP doesn't provide message IDs like Resend
|
||||
'provider': 'smtp',
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send email via SMTP: {str(e)}")
|
||||
|
||||
# Fallback to Django mail
|
||||
logger.info("Falling back to Django mail backend")
|
||||
return self._send_via_django(
|
||||
to=to,
|
||||
subject=subject,
|
||||
html=html,
|
||||
text=text,
|
||||
from_email=from_address.split('<')[-1].rstrip('>'),
|
||||
)
|
||||
|
||||
def _send_via_django(
|
||||
self,
|
||||
to: List[str],
|
||||
|
||||
@@ -27,6 +27,7 @@ class EmailSettingsAdmin(Igny8ModelAdmin):
|
||||
list_display = [
|
||||
'from_email',
|
||||
'from_name',
|
||||
'email_provider',
|
||||
'reply_to_email',
|
||||
'send_welcome_emails',
|
||||
'send_billing_emails',
|
||||
@@ -35,9 +36,26 @@ class EmailSettingsAdmin(Igny8ModelAdmin):
|
||||
readonly_fields = ['updated_at']
|
||||
|
||||
fieldsets = (
|
||||
('Email Provider', {
|
||||
'fields': ('email_provider',),
|
||||
'description': 'Select the active email service provider. Configure SMTP settings below if using SMTP.',
|
||||
}),
|
||||
('SMTP Configuration', {
|
||||
'fields': (
|
||||
'smtp_host',
|
||||
'smtp_port',
|
||||
'smtp_username',
|
||||
'smtp_password',
|
||||
'smtp_use_tls',
|
||||
'smtp_use_ssl',
|
||||
'smtp_timeout',
|
||||
),
|
||||
'description': 'SMTP server settings. Required when email_provider is set to SMTP.',
|
||||
'classes': ('collapse',),
|
||||
}),
|
||||
('Sender Configuration', {
|
||||
'fields': ('from_email', 'from_name', 'reply_to_email'),
|
||||
'description': 'Default sender settings. Email address must be verified in Resend.',
|
||||
'description': 'Default sender settings. Email address must be verified in Resend (if using Resend) or configured in SMTP server.',
|
||||
}),
|
||||
('Company Branding', {
|
||||
'fields': ('company_name', 'company_address', 'logo_url'),
|
||||
@@ -65,6 +83,114 @@ class EmailSettingsAdmin(Igny8ModelAdmin):
|
||||
}),
|
||||
)
|
||||
|
||||
change_form_template = 'admin/system/emailsettings/change_form.html'
|
||||
|
||||
def get_urls(self):
|
||||
"""Add custom URL for test email"""
|
||||
urls = super().get_urls()
|
||||
custom_urls = [
|
||||
path(
|
||||
'test-email/',
|
||||
self.admin_site.admin_view(self.test_email_view),
|
||||
name='system_emailsettings_test_email'
|
||||
),
|
||||
path(
|
||||
'send-test-email/',
|
||||
self.admin_site.admin_view(self.send_test_email),
|
||||
name='system_emailsettings_send_test'
|
||||
),
|
||||
]
|
||||
return custom_urls + urls
|
||||
|
||||
def test_email_view(self, request):
|
||||
"""Show test email form"""
|
||||
settings = EmailSettings.get_settings()
|
||||
|
||||
context = {
|
||||
**self.admin_site.each_context(request),
|
||||
'title': 'Send Test Email',
|
||||
'settings': settings,
|
||||
'opts': self.model._meta,
|
||||
'default_from_email': settings.from_email,
|
||||
'default_to_email': request.user.email,
|
||||
}
|
||||
|
||||
return render(request, 'admin/system/emailsettings/test_email.html', context)
|
||||
|
||||
def send_test_email(self, request):
|
||||
"""Send test email to verify configuration"""
|
||||
if request.method != 'POST':
|
||||
return JsonResponse({'error': 'POST required'}, status=405)
|
||||
|
||||
from django.utils import timezone
|
||||
from igny8_core.business.billing.services.email_service import EmailService
|
||||
|
||||
to_email = request.POST.get('to_email', request.user.email)
|
||||
subject = request.POST.get('subject', 'IGNY8 Test Email')
|
||||
|
||||
# Create fresh EmailService instance to pick up latest settings
|
||||
service = EmailService()
|
||||
settings = EmailSettings.get_settings()
|
||||
|
||||
test_html = f"""
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif; padding: 20px;">
|
||||
<h1 style="color: #6366f1;">IGNY8 Email Test</h1>
|
||||
<p>This is a test email to verify your email configuration.</p>
|
||||
|
||||
<h3>Configuration Details:</h3>
|
||||
<ul>
|
||||
<li><strong>Provider:</strong> {settings.email_provider.upper()}</li>
|
||||
<li><strong>From:</strong> {settings.from_name} <{settings.from_email}></li>
|
||||
<li><strong>Reply-To:</strong> {settings.reply_to_email}</li>
|
||||
<li><strong>Sent At:</strong> {timezone.now().strftime('%Y-%m-%d %H:%M:%S UTC')}</li>
|
||||
</ul>
|
||||
|
||||
<p style="color: #22c55e; font-weight: bold;">
|
||||
✓ If you received this email, your email configuration is working correctly!
|
||||
</p>
|
||||
|
||||
<hr style="margin: 20px 0; border: none; border-top: 1px solid #e5e7eb;">
|
||||
<p style="font-size: 12px; color: #6b7280;">
|
||||
This is an automated test email from IGNY8 Admin.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
try:
|
||||
result = service.send_transactional(
|
||||
to=to_email,
|
||||
subject=subject,
|
||||
html=test_html,
|
||||
tags=['test', 'admin-test'],
|
||||
)
|
||||
|
||||
if result.get('success'):
|
||||
# Log the test email
|
||||
EmailLog.objects.create(
|
||||
message_id=result.get('id', ''),
|
||||
to_email=to_email,
|
||||
from_email=settings.from_email,
|
||||
subject=subject,
|
||||
template_name='admin_test',
|
||||
status='sent',
|
||||
provider=result.get('provider', settings.email_provider),
|
||||
tags=['test', 'admin-test'],
|
||||
)
|
||||
|
||||
messages.success(
|
||||
request,
|
||||
f'Test email sent successfully to {to_email} via {result.get("provider", "unknown").upper()}!'
|
||||
)
|
||||
else:
|
||||
messages.error(request, f'Failed to send: {result.get("error", "Unknown error")}')
|
||||
|
||||
except Exception as e:
|
||||
messages.error(request, f'Error sending test email: {str(e)}')
|
||||
|
||||
return redirect(reverse('admin:system_emailsettings_changelist'))
|
||||
|
||||
def has_add_permission(self, request):
|
||||
"""Only allow one instance (singleton)"""
|
||||
return not EmailSettings.objects.exists()
|
||||
|
||||
@@ -16,6 +16,52 @@ class EmailSettings(models.Model):
|
||||
These settings work alongside IntegrationProvider (resend) configuration.
|
||||
"""
|
||||
|
||||
EMAIL_PROVIDER_CHOICES = [
|
||||
('resend', 'Resend'),
|
||||
('smtp', 'SMTP'),
|
||||
]
|
||||
|
||||
# Email provider selection
|
||||
email_provider = models.CharField(
|
||||
max_length=20,
|
||||
choices=EMAIL_PROVIDER_CHOICES,
|
||||
default='resend',
|
||||
help_text='Active email service provider'
|
||||
)
|
||||
|
||||
# SMTP Configuration
|
||||
smtp_host = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
help_text='SMTP server hostname (e.g., smtp.gmail.com)'
|
||||
)
|
||||
smtp_port = models.IntegerField(
|
||||
default=587,
|
||||
help_text='SMTP server port (587 for TLS, 465 for SSL, 25 for plain)'
|
||||
)
|
||||
smtp_username = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
help_text='SMTP authentication username'
|
||||
)
|
||||
smtp_password = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
help_text='SMTP authentication password'
|
||||
)
|
||||
smtp_use_tls = models.BooleanField(
|
||||
default=True,
|
||||
help_text='Use TLS encryption (recommended for port 587)'
|
||||
)
|
||||
smtp_use_ssl = models.BooleanField(
|
||||
default=False,
|
||||
help_text='Use SSL encryption (for port 465)'
|
||||
)
|
||||
smtp_timeout = models.IntegerField(
|
||||
default=30,
|
||||
help_text='SMTP connection timeout in seconds'
|
||||
)
|
||||
|
||||
# Default sender settings
|
||||
from_email = models.EmailField(
|
||||
default='noreply@igny8.com',
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Generated by Django 5.2.10 on 2026-01-08 06:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0020_add_email_models'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='emailsettings',
|
||||
name='email_provider',
|
||||
field=models.CharField(choices=[('resend', 'Resend'), ('smtp', 'SMTP')], default='resend', help_text='Active email service provider', max_length=20),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='emailsettings',
|
||||
name='smtp_host',
|
||||
field=models.CharField(blank=True, help_text='SMTP server hostname (e.g., smtp.gmail.com)', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='emailsettings',
|
||||
name='smtp_password',
|
||||
field=models.CharField(blank=True, help_text='SMTP authentication password', max_length=255),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='emailsettings',
|
||||
name='smtp_port',
|
||||
field=models.IntegerField(default=587, help_text='SMTP server port (587 for TLS, 465 for SSL, 25 for plain)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='emailsettings',
|
||||
name='smtp_timeout',
|
||||
field=models.IntegerField(default=30, help_text='SMTP connection timeout in seconds'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='emailsettings',
|
||||
name='smtp_use_ssl',
|
||||
field=models.BooleanField(default=False, help_text='Use SSL encryption (for port 465)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='emailsettings',
|
||||
name='smtp_use_tls',
|
||||
field=models.BooleanField(default=True, help_text='Use TLS encryption (recommended for port 587)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='emailsettings',
|
||||
name='smtp_username',
|
||||
field=models.CharField(blank=True, help_text='SMTP authentication username', max_length=255),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "admin/change_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block submit_buttons_bottom %}
|
||||
<div class="submit-row">
|
||||
<input type="submit" value="{% trans 'Save' %}" class="default" name="_save">
|
||||
<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue">
|
||||
|
||||
<a href="{% url 'admin:system_emailsettings_test_email' %}"
|
||||
class="button"
|
||||
style="float: right; background: #3b82f6; color: white; padding: 10px 20px; text-decoration: none; border-radius: 6px; margin-left: 10px;">
|
||||
📧 Send Test Email
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,97 @@
|
||||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div style="max-width: 800px; margin: 0 auto; padding: 20px;">
|
||||
<div style="background: white; border-radius: 8px; padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
|
||||
<h2 style="margin-top: 0; color: #1f2937;">Send Test Email</h2>
|
||||
|
||||
<div style="background: #f0f9ff; border-radius: 6px; padding: 16px; margin-bottom: 24px; border-left: 4px solid #3b82f6;">
|
||||
<h4 style="margin: 0 0 12px 0; color: #1e40af;">Current Configuration</h4>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
|
||||
<p style="margin: 0; color: #1e3a5f; font-size: 14px;">
|
||||
<strong>Provider:</strong>
|
||||
<span style="background: {% if settings.email_provider == 'resend' %}#dbeafe{% else %}#fef3c7{% endif %};
|
||||
padding: 2px 8px; border-radius: 4px;">
|
||||
{{ settings.email_provider|upper }}
|
||||
</span>
|
||||
</p>
|
||||
<p style="margin: 0; color: #1e3a5f; font-size: 14px;">
|
||||
<strong>From Email:</strong> {{ settings.from_email }}
|
||||
</p>
|
||||
<p style="margin: 0; color: #1e3a5f; font-size: 14px;">
|
||||
<strong>From Name:</strong> {{ settings.from_name }}
|
||||
</p>
|
||||
<p style="margin: 0; color: #1e3a5f; font-size: 14px;">
|
||||
<strong>Reply-To:</strong> {{ settings.reply_to_email }}
|
||||
</p>
|
||||
</div>
|
||||
{% if settings.email_provider == 'smtp' %}
|
||||
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid #bfdbfe;">
|
||||
<p style="margin: 0 0 4px 0; color: #1e3a5f; font-size: 14px;">
|
||||
<strong>SMTP Server:</strong> {{ settings.smtp_host }}:{{ settings.smtp_port }}
|
||||
</p>
|
||||
<p style="margin: 0; color: #1e3a5f; font-size: 14px;">
|
||||
<strong>Encryption:</strong>
|
||||
{% if settings.smtp_use_ssl %}SSL{% elif settings.smtp_use_tls %}TLS{% else %}None{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<form method="post" action="{% url 'admin:system_emailsettings_send_test' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="display: block; font-weight: 500; color: #374151; margin-bottom: 8px;">
|
||||
Send Test Email To:
|
||||
</label>
|
||||
<input type="email" name="to_email" value="{{ default_to_email }}"
|
||||
style="width: 100%; padding: 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px;"
|
||||
placeholder="recipient@example.com" required>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="display: block; font-weight: 500; color: #374151; margin-bottom: 8px;">
|
||||
Subject:
|
||||
</label>
|
||||
<input type="text" name="subject" value="IGNY8 Test Email"
|
||||
style="width: 100%; padding: 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px;">
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 12px;">
|
||||
<button type="submit"
|
||||
style="background: #6366f1; color: white; padding: 12px 24px; border: none;
|
||||
border-radius: 6px; font-weight: 500; cursor: pointer; font-size: 14px;">
|
||||
📧 Send Test Email
|
||||
</button>
|
||||
<a href="{% url 'admin:system_emailsettings_changelist' %}"
|
||||
style="background: #e5e7eb; color: #374151; padding: 12px 24px;
|
||||
border-radius: 6px; font-weight: 500; text-decoration: none; font-size: 14px;">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div style="background: #f0fdf4; border-radius: 8px; padding: 16px; margin-top: 20px; border-left: 4px solid #22c55e;">
|
||||
<h4 style="margin: 0 0 8px 0; color: #166534;">✅ What This Test Does</h4>
|
||||
<ul style="margin: 0; padding-left: 20px; color: #15803d; font-size: 14px;">
|
||||
<li>Sends a test email using your currently selected provider ({{ settings.email_provider|upper }})</li>
|
||||
<li>Verifies that your email configuration is working correctly</li>
|
||||
<li>Logs the email in Email Logs for tracking</li>
|
||||
<li>Shows the provider used and configuration details in the email body</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if settings.email_provider == 'smtp' and not settings.smtp_host %}
|
||||
<div style="background: #fef2f2; border-radius: 8px; padding: 16px; margin-top: 20px; border-left: 4px solid #ef4444;">
|
||||
<h4 style="margin: 0 0 8px 0; color: #991b1b;">⚠️ SMTP Not Configured</h4>
|
||||
<p style="margin: 0; color: #b91c1c; font-size: 14px;">
|
||||
You have selected SMTP as your email provider, but SMTP settings are not configured.
|
||||
Please go back and configure your SMTP server settings, or switch to Resend.
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -64,7 +64,11 @@ export default function ResetPassword() {
|
||||
// Correct API endpoint for password reset confirmation
|
||||
await fetchAPI('/v1/auth/password-reset/confirm/', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ token, new_password: password }),
|
||||
body: JSON.stringify({
|
||||
token,
|
||||
new_password: password,
|
||||
new_password_confirm: confirmPassword
|
||||
}),
|
||||
});
|
||||
setResetState('success');
|
||||
} catch (err: unknown) {
|
||||
|
||||
Reference in New Issue
Block a user