feat(hosting): signed preview URLs for POC sites
Replace the standalone POC viewer (duplicate rendering) with signed
JWT preview tokens that bypass StorefrontAccessMiddleware:
Architecture:
1. Admin clicks Preview → route generates signed JWT
2. Redirects to /storefront/{subdomain}/homepage?_preview=token
3. Middleware validates token signature + expiry + store_id
4. Sets request.state.is_preview = True, skips subscription check
5. Full storefront renders with HostWizard preview banner injected
New files:
- app/core/preview_token.py: create_preview_token/verify_preview_token
Changes:
- middleware/storefront_access.py: preview token bypass before sub check
- storefront/base.html: preview banner injection via is_preview state
- hosting/routes/pages/public.py: redirect with signed token (was direct render)
- hosting/routes/api/admin_sites.py: GET /sites/{id}/preview-url endpoint
Removed:
- hosting/templates/hosting/public/poc-viewer.html (replaced by storefront)
Benefits: one rendering path, all section types work, shareable 24h links.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,31 @@ def list_templates(
|
||||
)
|
||||
|
||||
|
||||
class PreviewUrlResponse(BaseModel):
|
||||
"""Response with signed preview URL."""
|
||||
|
||||
preview_url: str
|
||||
expires_in_hours: int = 24
|
||||
|
||||
|
||||
@router.get("/sites/{site_id}/preview-url", response_model=PreviewUrlResponse)
|
||||
def get_preview_url(
|
||||
site_id: int = Path(...),
|
||||
db: Session = Depends(get_db),
|
||||
current_admin: UserContext = Depends(get_current_admin_api),
|
||||
):
|
||||
"""Generate a signed preview URL for a hosted site."""
|
||||
from app.core.preview_token import create_preview_token
|
||||
|
||||
site = hosted_site_service.get_by_id(db, site_id)
|
||||
store = site.store
|
||||
subdomain = store.subdomain or store.store_code
|
||||
token = create_preview_token(store.id, subdomain, site.id)
|
||||
return PreviewUrlResponse(
|
||||
preview_url=f"/storefront/{subdomain}/?_preview={token}",
|
||||
)
|
||||
|
||||
|
||||
class BuildPocRequest(BaseModel):
|
||||
"""Request to build a POC site from prospect + template."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user