fix: correct tojson|safe usage in templates and update validator

- Remove |safe from |tojson in HTML attributes (x-data) - quotes must
  become " for browsers to parse correctly
- Update LANG-002 and LANG-003 architecture rules to document correct
  |tojson usage patterns:
  - HTML attributes: |tojson (no |safe)
  - Script blocks: |tojson|safe
- Fix validator to warn when |tojson|safe is used in x-data (breaks
  HTML attribute parsing)
- Improve code quality across services, APIs, and tests

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-13 22:59:51 +01:00
parent 94d268f330
commit 9920430b9e
123 changed files with 1408 additions and 840 deletions

View File

@@ -84,13 +84,17 @@ def init_log_settings():
)
if existing:
print(f"✓ Setting '{setting_data['key']}' already exists (value: {existing.value})")
print(
f"✓ Setting '{setting_data['key']}' already exists (value: {existing.value})"
)
updated_count += 1
else:
setting = AdminSetting(**setting_data)
db.add(setting)
created_count += 1
print(f"✓ Created setting '{setting_data['key']}' = {setting_data['value']}")
print(
f"✓ Created setting '{setting_data['key']}' = {setting_data['value']}"
)
db.commit()

View File

@@ -30,7 +30,6 @@ Environment Variables:
This script is idempotent when run normally.
"""
import argparse
import sys
from datetime import UTC, datetime
from decimal import Decimal
@@ -57,7 +56,9 @@ from models.database.company import Company
from models.database.customer import Customer, CustomerAddress
from models.database.marketplace_import_job import MarketplaceImportJob
from models.database.marketplace_product import MarketplaceProduct
from models.database.marketplace_product_translation import MarketplaceProductTranslation
from models.database.marketplace_product_translation import (
MarketplaceProductTranslation,
)
from models.database.order import Order, OrderItem
from models.database.product import Product
from models.database.user import User
@@ -263,7 +264,9 @@ def reset_all_data(db: Session):
sys.exit(0)
except EOFError:
print_error("No interactive terminal available.")
print(" Use FORCE_RESET=true to skip confirmation in non-interactive mode.")
print(
" Use FORCE_RESET=true to skip confirmation in non-interactive mode."
)
sys.exit(1)
# Delete in correct order (respecting foreign keys)
@@ -367,9 +370,7 @@ def create_demo_companies(db: Session, auth_manager: AuthManager) -> list[Compan
db.flush()
companies.append(company)
print_success(
f"Created company: {company.name} (Owner: {owner_user.email})"
)
print_success(f"Created company: {company.name} (Owner: {owner_user.email})")
db.flush()
return companies
@@ -456,7 +457,9 @@ def create_demo_vendors(
if vendor_data.get("custom_domain"):
domain = VendorDomain(
vendor_id=vendor.id,
domain=vendor_data["custom_domain"], # ✅ Field is 'domain', not 'domain_name'
domain=vendor_data[
"custom_domain"
], # ✅ Field is 'domain', not 'domain_name'
is_verified=True, # Auto-verified for demo
is_primary=True,
verification_token=None,
@@ -695,7 +698,7 @@ def print_summary(db: Session):
print(f" Vendors: {len(company.vendors) if company.vendors else 0}")
print(f" Status: {'✓ Active' if company.is_active else '✗ Inactive'}")
if company.is_verified:
print(f" Verified: ✓")
print(" Verified: ✓")
# Show vendor details
vendors = db.query(Vendor).all()

View File

@@ -18,6 +18,7 @@ from pathlib import Path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
def test_logging_endpoints():
"""Test logging-related API endpoints."""
print("\n" + "=" * 70)
@@ -34,7 +35,7 @@ def test_logging_endpoints():
# Create an exception log
try:
raise ValueError("Test exception for logging")
except Exception as e:
except Exception:
logging.error("Test exception logging", exc_info=True)
print(" ✓ Test logs created")
@@ -69,9 +70,12 @@ def test_logging_endpoints():
print(f" ✓ Database logs count: {count}")
if count > 0:
recent = db.query(ApplicationLog).order_by(
ApplicationLog.timestamp.desc()
).limit(5).all()
recent = (
db.query(ApplicationLog)
.order_by(ApplicationLog.timestamp.desc())
.limit(5)
.all()
)
print(" Recent logs:")
for log in recent:
@@ -90,9 +94,15 @@ def test_logging_endpoints():
db = SessionLocal()
try:
log_level = admin_settings_service.get_setting_value(db, "log_level", "INFO")
max_size = admin_settings_service.get_setting_value(db, "log_file_max_size_mb", 10)
retention = admin_settings_service.get_setting_value(db, "db_log_retention_days", 30)
log_level = admin_settings_service.get_setting_value(
db, "log_level", "INFO"
)
max_size = admin_settings_service.get_setting_value(
db, "log_file_max_size_mb", 10
)
retention = admin_settings_service.get_setting_value(
db, "db_log_retention_days", 30
)
print(f" ✓ Log Level: {log_level}")
print(f" ✓ Max File Size: {max_size} MB")
@@ -118,6 +128,7 @@ def test_logging_endpoints():
if __name__ == "__main__":
# Set up logging first
from app.core.logging import setup_logging
setup_logging()
success = test_logging_endpoints()

File diff suppressed because it is too large Load Diff

View File

@@ -56,7 +56,7 @@ class FileResult:
def status(self) -> str:
if self.errors > 0:
return "FAILED"
elif self.warnings > 0:
if self.warnings > 0:
return "PASSED*"
return "PASSED"
@@ -64,7 +64,7 @@ class FileResult:
def status_icon(self) -> str:
if self.errors > 0:
return ""
elif self.warnings > 0:
if self.warnings > 0:
return "⚠️"
return ""
@@ -220,17 +220,19 @@ class BaseValidator:
# Look for the function definition
for j in range(i + 1, min(i + 10, len(lines))):
next_line = lines[j].strip()
if next_line.startswith("def ") or next_line.startswith("async def "):
if next_line.startswith("def ") or next_line.startswith(
"async def "
):
# Extract function name
match = re.search(r"(?:async\s+)?def\s+(\w+)", next_line)
if match:
func_name = match.group(1)
results.append((i + 1, decorator, func_name))
break
elif next_line.startswith("@"):
if next_line.startswith("@"):
# Multiple decorators - continue to next
continue
elif next_line and not next_line.startswith("#"):
if next_line and not next_line.startswith("#"):
# Non-decorator, non-comment line - stop looking
break
i += 1
@@ -303,7 +305,7 @@ class BaseValidator:
Returns (is_valid, error_message) tuple.
"""
try:
with open(file_path, "r", encoding="utf-8") as f:
with open(file_path, encoding="utf-8") as f:
json.load(f)
return True, ""
except json.JSONDecodeError as e: