fix(email): Add SMTP configuration and fix invitation link routing

- Add SMTP email backend support to production settings (EMAIL_HOST, EMAIL_PORT, etc.)
- Falls back to console backend if SMTP not configured
- Fix AcceptInvitePage to support both path parameter (/accept-invite/:token) and
  query parameter (?token=...) formats for invitation tokens
- Add route for /accept-invite/:token in App.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-03 15:19:46 -05:00
parent 85c4b835fd
commit 39a376b39b
3 changed files with 19 additions and 6 deletions

View File

@@ -279,6 +279,7 @@ const AppContent: React.FC = () => {
<Route path="/oauth/callback/:provider" element={<OAuthCallback />} />
<Route path="/verify-email" element={<VerifyEmail />} />
<Route path="/accept-invite" element={<AcceptInvitePage />} />
<Route path="/accept-invite/:token" element={<AcceptInvitePage />} />
<Route path="/tenant-onboard" element={<TenantOnboardPage />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
@@ -322,6 +323,7 @@ const AppContent: React.FC = () => {
<Route path="/oauth/callback/:provider" element={<OAuthCallback />} />
<Route path="/verify-email" element={<VerifyEmail />} />
<Route path="/accept-invite" element={<AcceptInvitePage />} />
<Route path="/accept-invite/:token" element={<AcceptInvitePage />} />
<Route path="/tenant-onboard" element={<TenantOnboardPage />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { useSearchParams, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import {
useInvitationDetails,
@@ -23,8 +23,10 @@ import {
const AcceptInvitePage: React.FC = () => {
const { t } = useTranslation();
const [searchParams] = useSearchParams();
const { token: pathToken } = useParams<{ token: string }>();
const navigate = useNavigate();
const token = searchParams.get('token');
// Support both path parameter (/accept-invite/:token) and query parameter (?token=...)
const token = pathToken || searchParams.get('token');
const { data: invitation, isLoading, error } = useInvitationDetails(token);
const acceptInvitationMutation = useAcceptInvitation();

View File

@@ -168,10 +168,19 @@ if MAILGUN_API_KEY and MAILGUN_DOMAIN:
}
else:
# Fall back to SMTP or console email
EMAIL_BACKEND = env(
"DJANGO_EMAIL_BACKEND",
default="django.core.mail.backends.console.EmailBackend"
)
EMAIL_HOST = env("EMAIL_HOST", default="")
EMAIL_PORT = env.int("EMAIL_PORT", default=587)
EMAIL_HOST_USER = env("EMAIL_HOST_USER", default="")
EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD", default="")
EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", default=True)
if EMAIL_HOST and EMAIL_HOST_USER and EMAIL_HOST_PASSWORD:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
else:
EMAIL_BACKEND = env(
"DJANGO_EMAIL_BACKEND",
default="django.core.mail.backends.console.EmailBackend"
)
# LOGGING