feat: email verification, merchant/store password reset, seed gap fix
Some checks failed
Some checks failed
- Add EmailVerificationToken and UserPasswordResetToken models with migration - Add email verification flow: verify-email page route, resend-verification API - Block login for unverified users (EmailNotVerifiedException in auth_service) - Add forgot-password/reset-password endpoints for merchant and store auth - Add "Forgot Password?" links to merchant and store login pages - Send welcome email with verification link on merchant creation - Seed email_verification and merchant_password_reset email templates - Fix db-reset Makefile to run all init-prod seed scripts - Add UserAuthService to satisfy architecture validation rules - Add 52 new tests (unit + integration) with full coverage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,11 +6,12 @@ Merchant management endpoints for admin.
|
||||
import logging
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Path, Query
|
||||
from fastapi import APIRouter, Body, Depends, Path, Query, Request
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_api
|
||||
from app.core.database import get_db
|
||||
from app.core.environment import should_use_secure_cookies
|
||||
from app.modules.tenancy.exceptions import (
|
||||
ConfirmationRequiredException,
|
||||
MerchantHasStoresException,
|
||||
@@ -34,6 +35,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
@admin_merchants_router.post("", response_model=MerchantCreateResponse)
|
||||
def create_merchant_with_owner(
|
||||
request: Request,
|
||||
merchant_data: MerchantCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: UserContext = Depends(get_current_admin_api),
|
||||
@@ -44,7 +46,8 @@ def create_merchant_with_owner(
|
||||
This endpoint:
|
||||
1. Creates a new merchant record
|
||||
2. Creates an owner user account with owner_email (if not exists)
|
||||
3. Returns credentials (temporary password shown ONCE if new user created)
|
||||
3. Sends email verification + welcome email to owner
|
||||
4. Returns credentials (temporary password shown ONCE if new user created)
|
||||
|
||||
**Email Fields:**
|
||||
- `owner_email`: Used for owner's login/authentication (stored in users.email)
|
||||
@@ -58,6 +61,38 @@ def create_merchant_with_owner(
|
||||
|
||||
db.commit() # ✅ ARCH: Commit at API level for transaction control
|
||||
|
||||
# Send verification email to new owner (only for newly created users)
|
||||
if temp_password:
|
||||
try:
|
||||
from app.modules.messaging.services.email_service import EmailService
|
||||
from app.modules.tenancy.models import EmailVerificationToken
|
||||
|
||||
plaintext_token = EmailVerificationToken.create_for_user(db, owner_user.id)
|
||||
|
||||
scheme = "https" if should_use_secure_cookies() else "http"
|
||||
host = request.headers.get("host", "localhost:8000")
|
||||
verification_link = f"{scheme}://{host}/verify-email?token={plaintext_token}"
|
||||
|
||||
email_service = EmailService(db)
|
||||
email_service.send_template(
|
||||
template_code="email_verification",
|
||||
to_email=owner_user.email,
|
||||
to_name=owner_user.username,
|
||||
language="en",
|
||||
variables={
|
||||
"first_name": owner_user.username,
|
||||
"verification_link": verification_link,
|
||||
"expiry_hours": str(EmailVerificationToken.TOKEN_EXPIRY_HOURS),
|
||||
"platform_name": "Orion",
|
||||
},
|
||||
)
|
||||
|
||||
db.commit()
|
||||
logger.info(f"Verification email sent to {owner_user.email}")
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"Failed to send verification email: {e}") # noqa: SEC021
|
||||
|
||||
return MerchantCreateResponse(
|
||||
merchant=MerchantResponse(
|
||||
id=merchant.id,
|
||||
|
||||
Reference in New Issue
Block a user