feat: add platform detail/edit admin UI and service enhancements
- Add platform detail and edit admin pages with templates and JS - Add ContentPageService methods: list_all_platform_pages, list_all_vendor_defaults - Deprecate /admin/platform-homepage route (redirects to /admin/platforms) - Add migration to fix content_page nullable columns - Refine platform and vendor context middleware - Add platform context middleware unit tests - Update platforms.js with improved functionality - Add section-based homepage plan documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,12 +20,12 @@ import logging
|
||||
|
||||
from fastapi import Request
|
||||
from sqlalchemy.orm import Session
|
||||
# Note: We use pure ASGI middleware (not BaseHTTPMiddleware) to enable path rewriting
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.database import get_db
|
||||
from models.database.platform import Platform
|
||||
|
||||
# Note: We use pure ASGI middleware (not BaseHTTPMiddleware) to enable path rewriting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Default platform code for main marketing site
|
||||
@@ -139,7 +139,7 @@ class PlatformContextManager:
|
||||
platform = (
|
||||
db.query(Platform)
|
||||
.filter(Platform.domain == domain)
|
||||
.filter(Platform.is_active == True)
|
||||
.filter(Platform.is_active.is_(True))
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -148,8 +148,7 @@ class PlatformContextManager:
|
||||
f"[PLATFORM] Platform found via domain: {domain} → {platform.name}"
|
||||
)
|
||||
return platform
|
||||
else:
|
||||
logger.debug(f"[PLATFORM] No platform found for domain: {domain}")
|
||||
logger.debug(f"[PLATFORM] No platform found for domain: {domain}")
|
||||
|
||||
# Method 2: Path-prefix lookup
|
||||
if context.get("detection_method") == "path":
|
||||
@@ -161,7 +160,7 @@ class PlatformContextManager:
|
||||
.filter(
|
||||
(Platform.path_prefix == path_prefix) | (Platform.code == path_prefix)
|
||||
)
|
||||
.filter(Platform.is_active == True)
|
||||
.filter(Platform.is_active.is_(True))
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -170,15 +169,14 @@ class PlatformContextManager:
|
||||
f"[PLATFORM] Platform found via path prefix: {path_prefix} → {platform.name}"
|
||||
)
|
||||
return platform
|
||||
else:
|
||||
logger.debug(f"[PLATFORM] No platform found for path prefix: {path_prefix}")
|
||||
logger.debug(f"[PLATFORM] No platform found for path prefix: {path_prefix}")
|
||||
|
||||
# Method 3: Default lookup
|
||||
if context.get("detection_method") == "default":
|
||||
platform = (
|
||||
db.query(Platform)
|
||||
.filter(Platform.code == DEFAULT_PLATFORM_CODE)
|
||||
.filter(Platform.is_active == True)
|
||||
.filter(Platform.is_active.is_(True))
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -220,10 +218,7 @@ class PlatformContextManager:
|
||||
if host.startswith("admin."):
|
||||
return True
|
||||
|
||||
if path.startswith("/admin"):
|
||||
return True
|
||||
|
||||
return False
|
||||
return path.startswith("/admin")
|
||||
|
||||
@staticmethod
|
||||
def is_static_file_request(request: Request) -> bool:
|
||||
@@ -244,10 +239,7 @@ class PlatformContextManager:
|
||||
if any(path.startswith(static_path) for static_path in static_paths):
|
||||
return True
|
||||
|
||||
if "favicon.ico" in path:
|
||||
return True
|
||||
|
||||
return False
|
||||
return "favicon.ico" in path
|
||||
|
||||
|
||||
class PlatformContextMiddleware:
|
||||
@@ -432,18 +424,14 @@ class PlatformContextMiddleware:
|
||||
return True
|
||||
if any(path_lower.startswith(p) for p in static_paths):
|
||||
return True
|
||||
if "favicon.ico" in path_lower:
|
||||
return True
|
||||
return False
|
||||
return "favicon.ico" in path_lower
|
||||
|
||||
def _is_admin_request(self, path: str, host: str) -> bool:
|
||||
"""Check if request is for admin interface."""
|
||||
host_without_port = host.split(":")[0] if ":" in host else host
|
||||
if host_without_port.startswith("admin."):
|
||||
return True
|
||||
if path.startswith("/admin"):
|
||||
return True
|
||||
return False
|
||||
return path.startswith("/admin")
|
||||
|
||||
|
||||
def get_current_platform(request: Request) -> Platform | None:
|
||||
|
||||
@@ -91,7 +91,7 @@ class VendorContextManager:
|
||||
|
||||
# Method 3: Path-based detection (/vendor/vendorname/ or /vendors/vendorname/)
|
||||
# Support BOTH patterns for flexibility
|
||||
if path.startswith("/vendor/") or path.startswith("/vendors/"):
|
||||
if path.startswith(("/vendor/", "/vendors/")):
|
||||
# Determine which pattern
|
||||
if path.startswith("/vendors/"):
|
||||
prefix_len = len("/vendors/")
|
||||
@@ -133,8 +133,8 @@ class VendorContextManager:
|
||||
vendor_domain = (
|
||||
db.query(VendorDomain)
|
||||
.filter(VendorDomain.domain == domain)
|
||||
.filter(VendorDomain.is_active == True)
|
||||
.filter(VendorDomain.is_verified == True)
|
||||
.filter(VendorDomain.is_active.is_(True))
|
||||
.filter(VendorDomain.is_verified.is_(True))
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -157,7 +157,7 @@ class VendorContextManager:
|
||||
vendor = (
|
||||
db.query(Vendor)
|
||||
.filter(func.lower(Vendor.subdomain) == subdomain.lower())
|
||||
.filter(Vendor.is_active == True)
|
||||
.filter(Vendor.is_active.is_(True))
|
||||
.first()
|
||||
)
|
||||
|
||||
@@ -204,10 +204,7 @@ class VendorContextManager:
|
||||
if host.startswith("admin."):
|
||||
return True
|
||||
|
||||
if path.startswith("/admin"):
|
||||
return True
|
||||
|
||||
return False
|
||||
return path.startswith("/admin")
|
||||
|
||||
@staticmethod
|
||||
def is_api_request(request: Request) -> bool:
|
||||
@@ -263,9 +260,7 @@ class VendorContextManager:
|
||||
|
||||
# Method 1: Path-based detection from referer path
|
||||
# /vendors/wizamart/shop/products → wizamart
|
||||
if referer_path.startswith("/vendors/") or referer_path.startswith(
|
||||
"/vendor/"
|
||||
):
|
||||
if referer_path.startswith(("/vendors/", "/vendor/")):
|
||||
prefix = (
|
||||
"/vendors/" if referer_path.startswith("/vendors/") else "/vendor/"
|
||||
)
|
||||
@@ -384,10 +379,7 @@ class VendorContextManager:
|
||||
if any(path.startswith(static_path) for static_path in static_paths):
|
||||
return True
|
||||
|
||||
if "favicon.ico" in path:
|
||||
return True
|
||||
|
||||
return False
|
||||
return "favicon.ico" in path
|
||||
|
||||
|
||||
class VendorContextMiddleware(BaseHTTPMiddleware):
|
||||
|
||||
Reference in New Issue
Block a user