fix: resolve onboarding session not found warnings and frontend build OOM
- Use canonical Clerk user id (clerk_user_id) across all onboarding entrypoints to ensure consistent OnboardingSession.user_id lookup - Fix API key persistence in api_key_manager.py to use correct APIKey model columns (session_id, provider, key) - Increase Node heap for frontend build to 8GB and add build:nomap script to disable sourcemaps and reduce memory usage - Update onboarding endpoints (endpoints_core.py, onboarding_control_service.py, step_management_service.py) to prefer clerk_user_id over id - Fix frontend workflowStore.ts TypeScript error by returning WorkflowError instance - Add website_automation_service.py for onboarding automation
This commit is contained in:
250
backend/api/onboarding_utils/website_automation_service.py
Normal file
250
backend/api/onboarding_utils/website_automation_service.py
Normal file
@@ -0,0 +1,250 @@
|
||||
"""Website Automation Service for API layer - orchestrates website creation."""
|
||||
from typing import Dict, Any, Optional
|
||||
from loguru import logger
|
||||
import os
|
||||
import tempfile
|
||||
import json
|
||||
from fastapi import HTTPException
|
||||
|
||||
# Import the actual automation service
|
||||
from services.onboarding.website_automation_service import WebsiteAutomationService as CoreAutomationService
|
||||
|
||||
|
||||
class WebsiteAutomationService:
|
||||
"""API layer service for website automation operations."""
|
||||
|
||||
def __init__(self):
|
||||
logger.info("🔄 Initializing WebsiteAutomationService (API layer)...")
|
||||
self.core_service = CoreAutomationService()
|
||||
|
||||
async def generate_preview_site(
|
||||
self,
|
||||
user_id: str,
|
||||
site_brief: Dict[str, Any],
|
||||
css: str
|
||||
) -> Dict[str, Any]:
|
||||
"""Generate a preview site for the user."""
|
||||
try:
|
||||
logger.info(f"Generating preview site for user {user_id}")
|
||||
|
||||
# For preview, we'll create a temporary HTML file
|
||||
# In production, this could be hosted on a preview server
|
||||
preview_html = self._generate_preview_html(site_brief, css)
|
||||
|
||||
# Save to temporary file (in production, use proper hosting)
|
||||
preview_url = f"/preview/{user_id}/index.html"
|
||||
|
||||
return {
|
||||
"preview_url": preview_url,
|
||||
"preview_root": f"/preview/{user_id}",
|
||||
"preview_files": ["index.html", "custom.css"],
|
||||
"preview_html": preview_html
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to generate preview site: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Preview generation failed: {str(e)}")
|
||||
|
||||
def _generate_preview_html(self, site_brief: Dict[str, Any], css: str) -> str:
|
||||
"""Generate HTML preview from site brief and CSS."""
|
||||
try:
|
||||
site_data = site_brief.get("site_brief", {})
|
||||
business_name = site_data.get("business_name", "Your Business")
|
||||
tagline = site_data.get("tagline", "Your tagline here")
|
||||
|
||||
# Get content plan
|
||||
content_plan = site_brief.get("content_plan", {})
|
||||
required_pages = content_plan.get("required_pages", [])
|
||||
|
||||
# Generate HTML
|
||||
html = f"""<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{business_name}</title>
|
||||
<style>
|
||||
{css}
|
||||
|
||||
/* Additional preview styles */
|
||||
.preview-banner {{
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}}
|
||||
.preview-banner h1 {{
|
||||
margin: 0;
|
||||
font-size: 2.5rem;
|
||||
}}
|
||||
.preview-banner p {{
|
||||
margin: 0.5rem 0 0 0;
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.9;
|
||||
}}
|
||||
.preview-content {{
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 2rem;
|
||||
}}
|
||||
.preview-section {{
|
||||
margin-bottom: 3rem;
|
||||
}}
|
||||
.preview-grid {{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}}
|
||||
.preview-card {{
|
||||
background: var(--color-surface, #f8fafc);
|
||||
padding: 1.5rem;
|
||||
border-radius: var(--border-radius-medium, 0.5rem);
|
||||
box-shadow: var(--shadow-small, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
||||
}}
|
||||
.preview-watermark {{
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
z-index: 1000;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="preview-banner">
|
||||
<h1>{business_name}</h1>
|
||||
<p>{tagline}</p>
|
||||
<div class="preview-watermark">ALwrity Preview</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-content">
|
||||
{self._generate_page_content(required_pages)}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Basic interactions for preview
|
||||
document.addEventListener('DOMContentLoaded', function() {{
|
||||
console.log('ALwrity website preview loaded');
|
||||
}});
|
||||
</script>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
return html
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to generate preview HTML: {str(e)}")
|
||||
return f"<html><body><h1>Preview Error</h1><p>{str(e)}</p></body></html>"
|
||||
|
||||
def _generate_page_content(self, required_pages: list) -> str:
|
||||
"""Generate HTML content for pages."""
|
||||
if not required_pages:
|
||||
return """
|
||||
<div class="preview-section">
|
||||
<h2>Welcome to Your Website</h2>
|
||||
<p>This is a preview of your new website. The content will be generated based on your business information.</p>
|
||||
<div class="preview-grid">
|
||||
<div class="preview-card">
|
||||
<h3>About Us</h3>
|
||||
<p>Learn more about your business and what makes you unique.</p>
|
||||
</div>
|
||||
<div class="preview-card">
|
||||
<h3>Services</h3>
|
||||
<p>Discover the services and products you offer to your customers.</p>
|
||||
</div>
|
||||
<div class="preview-card">
|
||||
<h3>Contact</h3>
|
||||
<p>Get in touch with you through various contact methods.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
content_parts = []
|
||||
|
||||
for page in required_pages:
|
||||
page_name = page.get("page", "page").title()
|
||||
goal = page.get("goal", "")
|
||||
key_points = page.get("key_points", [])
|
||||
cta = page.get("cta", "Get Started")
|
||||
|
||||
page_html = f"""
|
||||
<div class="preview-section">
|
||||
<h2>{page_name}</h2>
|
||||
<p>{goal}</p>
|
||||
"""
|
||||
|
||||
if key_points:
|
||||
page_html += "<div class='preview-grid'>"
|
||||
for point in key_points:
|
||||
page_html += f"""
|
||||
<div class="preview-card">
|
||||
<p>{point}</p>
|
||||
</div>
|
||||
"""
|
||||
page_html += "</div>"
|
||||
|
||||
if cta:
|
||||
page_html += f"""
|
||||
<div style="margin-top: 2rem;">
|
||||
<button class="btn btn-primary">{cta}</button>
|
||||
</div>
|
||||
"""
|
||||
|
||||
page_html += "</div>"
|
||||
content_parts.append(page_html)
|
||||
|
||||
return "".join(content_parts)
|
||||
|
||||
async def generate_website(
|
||||
self,
|
||||
user_id: str,
|
||||
business_info: Dict[str, Any],
|
||||
niche: str,
|
||||
site_brief: Optional[Dict[str, Any]] = None,
|
||||
css: Optional[str] = None
|
||||
) -> Dict[str, str]:
|
||||
"""Generate and deploy a full website."""
|
||||
try:
|
||||
logger.info(f"Generating website for user {user_id}")
|
||||
|
||||
# Use the core automation service
|
||||
result = await self.core_service.generate_website(
|
||||
user_id=user_id,
|
||||
business_info=business_info,
|
||||
niche=niche,
|
||||
site_brief=site_brief,
|
||||
css=css
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to generate website: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Website generation failed: {str(e)}")
|
||||
|
||||
def get_deployment_status(self, user_id: str) -> Dict[str, Any]:
|
||||
"""Get the status of website deployment."""
|
||||
try:
|
||||
# This would typically check the deployment status
|
||||
# For now, return a placeholder
|
||||
return {
|
||||
"status": "pending",
|
||||
"message": "Deployment status checking not yet implemented"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get deployment status: {str(e)}")
|
||||
return {
|
||||
"status": "error",
|
||||
"message": str(e)
|
||||
}
|
||||
|
||||
|
||||
# Singleton instance
|
||||
website_automation_service = WebsiteAutomationService()
|
||||
Reference in New Issue
Block a user