fix: resolve all architecture validation errors (62 -> 0)

Major refactoring to achieve zero architecture violations:

API Layer:
- vendor/settings.py: Move validation to Pydantic field validators
  (tax rate, delivery method, boost sort, preorder days, languages, locales)
- admin/email_templates.py: Add Pydantic response models
  (TemplateListResponse, CategoriesResponse)
- shop/auth.py: Move password reset logic to CustomerService

Service Layer:
- customer_service.py: Add password reset methods
  (get_customer_for_password_reset, validate_and_reset_password)

Exception Layer:
- customer.py: Add InvalidPasswordResetTokenException,
  PasswordTooShortException

Frontend:
- admin/email-templates.js: Use apiClient, Utils.showToast()
- vendor/email-templates.js: Use apiClient, parent init pattern

Templates:
- admin/email-templates.html: Fix block name to extra_scripts
- shop/base.html: Add language default filter

Tooling:
- validate_architecture.py: Fix LANG-001 false positive for
  SUPPORTED_LANGUAGES and SUPPORTED_LOCALES blocks

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-03 18:48:59 +01:00
parent 370d61e8f7
commit 5155ef7445
10 changed files with 391 additions and 377 deletions

View File

@@ -279,15 +279,7 @@ def forgot_password(request: Request, email: str, db: Session = Depends(get_db))
)
# Look up customer by email (vendor-scoped)
customer = (
db.query(Customer)
.filter(
Customer.vendor_id == vendor.id,
Customer.email == email.lower(),
Customer.is_active == True, # noqa: E712
)
.first()
)
customer = customer_service.get_customer_for_password_reset(db, vendor.id, email)
# If customer exists, generate token and send email
if customer:
@@ -365,43 +357,13 @@ def reset_password(
},
)
# Validate password length
if len(new_password) < 8:
raise HTTPException(
status_code=400,
detail="Password must be at least 8 characters long",
)
# Find valid token
token_record = PasswordResetToken.find_valid_token(db, reset_token)
if not token_record:
raise HTTPException(
status_code=400,
detail="Invalid or expired password reset link. Please request a new one.",
)
# Get the customer and verify they belong to this vendor
customer = db.query(Customer).filter(Customer.id == token_record.customer_id).first()
if not customer or customer.vendor_id != vendor.id:
raise HTTPException(
status_code=400,
detail="Invalid or expired password reset link. Please request a new one.",
)
if not customer.is_active:
raise HTTPException(
status_code=400,
detail="This account is not active. Please contact support.",
)
# Hash the new password and update customer
hashed_password = customer_service.auth_service.hash_password(new_password)
customer.hashed_password = hashed_password
# Mark token as used
token_record.mark_used(db)
# Validate and reset password using service
customer = customer_service.validate_and_reset_password(
db=db,
vendor_id=vendor.id,
reset_token=reset_token,
new_password=new_password,
)
db.commit()