diff --git "a/app.py" "b/app.py"
--- "a/app.py"
+++ "b/app.py"
@@ -1,1636 +1,1904 @@
-from fastapi import FastAPI, HTTPException, Response
-from fastapi.responses import HTMLResponse
-from pydantic import BaseModel, validator
-import requests
-import os
-import base64
-import json
-from typing import Optional, List
-import random
-import time
-from datetime import datetime
-import psutil
-import logging
-
-# Configure logging
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger(__name__)
-
-app = FastAPI(title="MOFH API Proxy + IONA AI")
-
-# Track uptime
-START_TIME = time.time()
-REQUEST_COUNT = {"total": 0, "generate": 0, "create": 0, "errors": 0}
-
-# High-quality image library for professional websites
-WEBSITE_IMAGES = [
- "https://picsum.photos/id/1/1920/1080",
- "https://picsum.photos/id/2/1920/1080",
- "https://picsum.photos/id/3/1920/1080",
- "https://picsum.photos/id/4/1920/1080",
- "https://picsum.photos/id/5/1920/1080",
- "https://picsum.photos/id/6/1920/1080",
- "https://picsum.photos/id/7/1920/1080",
- "https://picsum.photos/id/8/1920/1080",
- "https://picsum.photos/id/9/1920/1080",
- "https://picsum.photos/id/36/1920/1080",
- "https://picsum.photos/id/37/1920/1080",
- "https://picsum.photos/id/38/1920/1080",
- "https://picsum.photos/id/39/1920/1080",
- "https://picsum.photos/id/40/1920/1080",
- "https://picsum.photos/id/41/1920/1080",
- "https://picsum.photos/id/42/1920/1080",
- "https://picsum.photos/id/43/1920/1080",
- "https://picsum.photos/id/44/1920/1080",
- "https://picsum.photos/id/45/1920/1080",
- "https://picsum.photos/id/46/1920/1080",
- "https://picsum.photos/id/47/1920/1080",
- "https://picsum.photos/id/48/1920/1080",
- "https://picsum.photos/id/49/1920/1080",
- "https://picsum.photos/id/50/1920/1080",
- "https://picsum.photos/id/51/1920/1080",
- "https://picsum.photos/id/52/1920/1080",
- "https://picsum.photos/id/53/1920/1080",
- "https://picsum.photos/id/54/1920/1080",
- "https://picsum.photos/id/55/1920/1080",
- "https://picsum.photos/id/56/1920/1080",
- "https://picsum.photos/id/57/1920/1080",
- "https://picsum.photos/id/58/1920/1080",
- "https://picsum.photos/id/59/1920/1080",
- "https://picsum.photos/id/60/1920/1080",
- "https://images.unsplash.com/photo-1497366754035-f200968a6e72?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1497366811353-6870744d04b2?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1497215728101-856f4ea42174?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1441986300917-64674bd600d8?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1504384308090-c894fdcc538d?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1519389950473-47ba0277781c?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1542744094-24638eff58bb?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1460925895917-afdab827c52f?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1552664730-d307ca884978?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1531482615713-2afd69097998?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&q=80&w=1920&h=1080",
- "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&q=80&w=1920&h=1080"
-]
-
-def get_random_images(count=10):
- """Get random images from the library"""
- return random.sample(WEBSITE_IMAGES, min(count, len(WEBSITE_IMAGES)))
-
-MOFH_API_URL = "https://panel.myownfreehost.net:2087/xml-api/"
-MOFH_API_USERNAME = "ZqHK1H5BBnrrywnsXXW2pY42QNqDxHru74HtwZofkn1LRawd4TDcKCPnIViBiPaJg1sUXUlKErn4EhXmqu0vx1STF263TxiTQuyKzYcErjSax5W4lBoLXqpE0aQqnLLKi8Pk7fncTbcfwqnKVRNOzLd34xdakdiHH1Z4gmDbetSmYauCId4Cj8SVizSBTuN6TMIrz2oMKAoXPypSocVOOUI0rSOIld34MY6yXtcHdSgh3Jd5J8mgM8SiW8BvHtE"
-MOFH_API_PASSWORD = "Uj39SyF5okKP73Ml6laLEay40MZEXSe81lStBEc9dQC2odK49Tw4K5l4BXI3DfnIVA5ZoIH0GFKvBRBO1cLIU1djjSBCABBkp2GO2oeH4BROw238dZwODu0kklR0uezHPfezHMCbcbjSKLRKJX5Ns7gZ9wgXxZbO3e8Rf4MoZnQVk5IUNLvKPWHDP43PsQnAFAYOhDNnKPBBg2CoHXsJ1Kq8C5wRuBd64GPyPhY7sbSCibAMUWKRkUSzRi7aq5i"
-
-# IONA AI - Multiple API Keys for Load Balancing (No Rate Limits)
-GROQ_API_KEYS = [
- "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", # Replace with real keys
- "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",
- "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",
- "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"
-]
-
-# Model Configuration - Use ONLY the most capable model
-AI_MODELS = {
- "groq": [
- "llama-3.3-70b-versatile", # Most capable, reliable for complex generation
- ]
-}
-
-# Track API usage for rotation
-api_usage_counter = {key: 0 for key in GROQ_API_KEYS}
-
-def get_next_api_key():
- """Round-robin API key selection for load balancing"""
- min_usage_key = min(api_usage_counter, key=api_usage_counter.get)
- api_usage_counter[min_usage_key] += 1
- return min_usage_key
-
-def get_random_model():
- """Select model (always use 70B for reliability)"""
- return AI_MODELS["groq"][0] # Always use llama-3.3-70b-versatile
-
-class AIWebsiteRequest(BaseModel):
- prompt: str
- business_type: Optional[str] = "general"
- color_scheme: Optional[str] = "modern"
- include_database: Optional[bool] = True
- php_only: Optional[bool] = False
- features: Optional[List[str]] = []
-
- @validator('prompt')
- def validate_prompt(cls, v):
- if not v or len(v.strip()) < 10:
- raise ValueError('Prompt must be at least 10 characters')
- if len(v) > 10000:
- raise ValueError('Prompt too long (max 10000 characters)')
- return v.strip()
-
-class AccountRequest(BaseModel):
- username: str
- password: str
- email: str
- domain: str
- plan: str = "free"
-
- @validator('username')
- def validate_username(cls, v):
- if not v or len(v) < 3:
- raise ValueError('Username must be at least 3 characters')
- if len(v) > 20:
- raise ValueError('Username too long (max 20 characters)')
- return v.strip()
-
- @validator('email')
- def validate_email(cls, v):
- if '@' not in v or '.' not in v:
- raise ValueError('Invalid email format')
- return v.strip()
-
-@app.get("/", response_class=HTMLResponse)
-def root():
- """Uptime dashboard with external IP display"""
- import socket
-
- hostname = socket.gethostname()
- local_ip = socket.gethostbyname(hostname)
-
- # Get external IP
- try:
- external_ip = requests.get('https://api.ipify.org', timeout=5).text
- except:
- external_ip = "Unable to detect"
-
- # Calculate uptime
- uptime_seconds = int(time.time() - START_TIME)
- uptime_hours = uptime_seconds // 3600
- uptime_minutes = (uptime_seconds % 3600) // 60
- uptime_secs = uptime_seconds % 60
-
- # Get system stats
- try:
- cpu_percent = psutil.cpu_percent(interval=1)
- memory = psutil.virtual_memory()
- memory_percent = memory.percent
- memory_used = memory.used / (1024**3) # GB
- memory_total = memory.total / (1024**3) # GB
- except:
- cpu_percent = 0
- memory_percent = 0
- memory_used = 0
- memory_total = 0
-
- # API key status
- api_key_usage = []
- for key, count in api_usage_counter.items():
- api_key_usage.append(f"{key[:20]}... → {count} requests")
-
- html_content = f"""
-
-
-
-
-
- IONA AI - System Status
-
-
-
-
-
-
-
-
-
⏱️ Uptime
-
- Running Since
- {datetime.fromtimestamp(START_TIME).strftime('%Y-%m-%d %H:%M:%S')}
-
-
- Uptime
- {uptime_hours}h {uptime_minutes}m {uptime_secs}s
-
-
- Total Requests
- {REQUEST_COUNT['total']}
-
-
- Errors
- {REQUEST_COUNT['errors']}
-
-
-
-
-
🌐 Network Info
-
- Hostname
- {hostname}
-
-
- Local IP
- {local_ip}
-
-
-
External IP Address
-
{external_ip}
-
-
- ⚠️ Add this IP to MOFH API whitelist
-
-
-
-
-
💻 System Resources
-
- CPU Usage
- {cpu_percent:.1f}%
-
-
-
- Memory Usage
- {memory_percent:.1f}%
-
-
-
{memory_percent:.1f}%
-
-
- Memory
- {memory_used:.2f} GB / {memory_total:.2f} GB
-
-
-
-
-
-
-
🤖 AI Configuration
-
- Active Models
- {len(AI_MODELS['groq'])}
-
-
- API Keys
- {len(GROQ_API_KEYS)}
-
-
- Generations
- {REQUEST_COUNT['generate']}
-
-
- {''.join([f'
{usage}
' for usage in api_key_usage])}
-
-
-
-
-
-
-
👨💻 Developer Info
-
- Developer
- Pratyush Srivastava
-
-
-
-
- Version
- 2026.1.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- """
-
- return html_content
-
-@app.get("/health")
-def health_check():
- """Health check endpoint for monitoring"""
- return {
- "status": "healthy",
- "uptime_seconds": int(time.time() - START_TIME),
- "timestamp": datetime.now().isoformat(),
- "requests": REQUEST_COUNT
- }
-
-@app.get("/test")
-def test_generation():
- """Test AI generation with a simple prompt"""
- try:
- api_key = get_next_api_key()
- model = get_random_model()
-
- test_prompt = "Create a simple HTML page with a heading 'Hello World' and a paragraph."
-
- headers = {
- "Authorization": f"Bearer {api_key}",
- "Content-Type": "application/json"
- }
-
- payload = {
- "model": model,
- "messages": [
- {"role": "system", "content": "You are a helpful assistant. Return only valid JSON."},
- {"role": "user", "content": test_prompt}
- ],
- "temperature": 0.7,
- "max_tokens": 500
- }
-
- response = requests.post(
- "https://api.groq.com/openai/v1/chat/completions",
- headers=headers,
- json=payload,
- timeout=30
- )
-
- if response.status_code == 200:
- result = response.json()
- return {
- "success": True,
- "model": model,
- "response_length": len(result['choices'][0]['message']['content']),
- "tokens_used": result.get('usage', {}),
- "message": "AI is working correctly!"
- }
- else:
- return {
- "success": False,
- "error": f"HTTP {response.status_code}",
- "message": "AI test failed"
- }
- except Exception as e:
- return {
- "success": False,
- "error": str(e),
- "message": "AI test failed with exception"
- }
-
-@app.post("/generate-website")
-def generate_website(req: AIWebsiteRequest):
- """IONA AI - Generate complete website with HTML, CSS, JS, and MySQL"""
- REQUEST_COUNT["total"] += 1
- REQUEST_COUNT["generate"] += 1
-
- try:
- logger.info("=== IONA AI Generation Started ===")
- logger.info(f"Business Type: {req.business_type}")
- logger.info(f"Prompt Length: {len(req.prompt)} chars")
- logger.info(f"PHP Only Mode: {req.php_only}")
-
- api_key = get_next_api_key()
- model = get_random_model()
- logger.info(f"Model: {model}, API Key: {api_key[:20]}...")
-
- # Get random images for this website
- selected_images = get_random_images(15)
- images_list = '\n'.join([f"- {img}" for img in selected_images])
-
- # Check if PHP-only mode
- if req.php_only:
- # PHP-only generation prompt
- system_prompt = f"""You are IONA AI, an expert PHP backend developer.
-
-🎯 HOSTING ENVIRONMENT: VistaPanel (Free Hosting)
-- PHP Version: 7.4 or 8.0
-- MySQL Database available
-- PDO and MySQLi supported
-
-🎯 MISSION: Generate ONLY PHP BACKEND FILES (NO HTML/CSS/JS).
-
-📦 REQUIRED OUTPUT FORMAT:
-Return ONLY a valid JSON object with these keys:
-{{
- "contact_php": "Complete contact form handler with validation and email sending",
- "api_php": "RESTful API endpoints with JSON responses",
- "functions_php": "Helper functions for database, validation, sanitization",
- "config": "Database configuration with PDO connection",
- "sql": "MySQL schema with sample data",
- "readme": "Setup guide for VistaPanel"
-}}
-
-🐘 PHP FILE REQUIREMENTS:
-
-1. **contact_php** (Contact Form Handler):
- - Handle POST requests
- - Validate: name, email, phone, message
- - Sanitize all inputs (htmlspecialchars, filter_var)
- - Send email using mail() function
- - Store in database
- - Return JSON response
- - CSRF protection
- - Rate limiting (session-based)
- - Error handling
-
-2. **api_php** (API Endpoints):
- - RESTful structure
- - GET /api.php?action=list (list items)
- - POST /api.php?action=create (create item)
- - PUT /api.php?action=update (update item)
- - DELETE /api.php?action=delete (delete item)
- - JSON responses
- - Authentication (if needed)
- - Input validation
- - Error handling
-
-3. **functions_php** (Helper Functions):
- - Database query helpers (select, insert, update, delete)
- - Input validation functions
- - Sanitization functions
- - Email sending function
- - Authentication helpers
- - Session management
- - Error logging
-
-4. **config** (Database Configuration):
- - PDO connection setup
- - Database credentials (localhost, username, password, dbname)
- - Error handling (try-catch)
- - Timezone settings
- - Error reporting settings
- - Session configuration
-
-5. **sql** (MySQL Schema):
- - CREATE DATABASE statement
- - CREATE TABLE statements
- - Proper data types (VARCHAR, INT, TEXT, DATETIME)
- - Primary keys (AUTO_INCREMENT)
- - Foreign keys and indexes
- - Timestamps (created_at, updated_at)
- - Sample data (5-10 realistic entries)
-
-PHP SECURITY REQUIREMENTS:
-- Use prepared statements (PDO) for ALL database queries
-- Sanitize ALL user inputs
-- Validate email addresses with filter_var()
-- Use password_hash() for passwords
-- Implement CSRF tokens
-- Use htmlspecialchars() for output
-- Set proper error reporting
-- Use sessions securely
-
-PHP BEST PRACTICES:
-- PSR-12 coding standards
-- Proper indentation (4 spaces)
-- Clear variable names
-- Comments for complex logic
-- Separate concerns
-- Return JSON for AJAX requests
-- Use HTTP status codes
-- Error handling (try-catch)
-
-CODE FORMATTING:
-- Proper indentation (4 spaces per level)
-- Blank lines between functions
-- Comments for each function
-- Use \n for newlines in JSON strings
-
-IMPORTANT JSON RULES:
-- Return ONLY the JSON object
-- NO markdown code blocks
-- NO explanations
-- Escape quotes with backslash
-- Use \n for newlines
-- Do NOT return null values
-- Generate COMPLETE, FUNCTIONAL code
-
-CREATE PROFESSIONAL PHP BACKEND FOR VISTAPANEL! 🚀"""
-
- user_prompt = f"""Generate PHP backend files for: {req.prompt}
-
-Requirements:
-- Complete contact form handler
-- RESTful API endpoints
-- Helper functions
-- Database configuration
-- MySQL schema with sample data
-
-Return as JSON with contact_php, api_php, functions_php, config, sql, and readme keys."""
- else:
- # Full website generation prompt
- system_prompt = f"""You are IONA AI, an elite full-stack web developer with 15+ years of experience.
-
-🎯 HOSTING ENVIRONMENT: VistaPanel (Free Hosting)
-- PHP Version: 7.4 or 8.0
-- MySQL Database available
-- File Manager: VistaPanel
-- No SSH access
-- Standard PHP functions available
-- PDO and MySQLi supported
-
-🎯 MISSION: Generate COMPLETE, PRODUCTION-READY, MULTI-FILE websites with REAL CONTENT.
-
-📦 REQUIRED OUTPUT FORMAT:
-Return ONLY a valid JSON object with these keys:
-{{
- "html": "Complete HTML5 document with PROPER INDENTATION (2 spaces per level)",
- "css": "Complete CSS with PROPER FORMATTING and line breaks",
- "js": "Complete JavaScript with PROPER FORMATTING",
- "php": "PHP backend files (contact.php, api.php, etc.) with PROPER FORMATTING",
- "sql": "MySQL schema with sample data (if database needed)",
- "config": "PHP config file with database connection (if database needed)",
- "readme": "Setup and customization guide for VistaPanel"
-}}
-
-🖼️ USE THESE REAL IMAGES (randomly selected for this website):
-{images_list}
-
-IMPORTANT IMAGE USAGE:
-- Use different images for hero, gallery, team, testimonials, blog posts
-- Add proper alt text describing each image
-- Use responsive image techniques (srcset if needed)
-- Images are high-quality 1920x1080, perfect for hero sections
-
-✨ CODE FORMATTING RULES (CRITICAL):
-1. HTML: Proper indentation with 2 spaces per level
-2. CSS: One rule per line, organized by sections with comments
-3. JavaScript: Proper function formatting with line breaks
-4. PHP: PSR-12 coding standards, proper indentation
-5. NO MINIFIED CODE - Make it readable and maintainable
-6. Add blank lines between major sections
-7. Use \n for newlines in JSON strings
-8. Properly indent nested elements
-
-🐘 PHP FILE GENERATION (IMPORTANT):
-When generating PHP files, create COMPLETE, FUNCTIONAL code:
-
-1. **contact.php** (Contact Form Handler):
- - Validate all inputs (name, email, message)
- - Sanitize data (htmlspecialchars, filter_var)
- - Send email using mail() function
- - Store in database if requested
- - Return JSON response
- - Include CSRF protection
- - Rate limiting (session-based)
-
-2. **config.php** (Database Configuration):
- - PDO connection setup
- - Error handling (try-catch)
- - Database credentials (localhost, username, password, dbname)
- - Timezone settings
- - Error reporting settings
- - Session configuration
-
-3. **functions.php** (Helper Functions):
- - Database query helpers
- - Input validation functions
- - Sanitization functions
- - Email sending function
- - Authentication helpers (if needed)
-
-4. **api.php** (API Endpoints):
- - RESTful API structure
- - JSON responses
- - Error handling
- - Input validation
- - CORS headers (if needed)
-
-PHP SECURITY REQUIREMENTS:
-- Use prepared statements (PDO) for all database queries
-- Sanitize ALL user inputs
-- Validate email addresses with filter_var()
-- Use password_hash() for passwords
-- Implement CSRF tokens
-- Use htmlspecialchars() for output
-- Set proper error reporting
-- Use sessions securely
-
-PHP BEST PRACTICES:
-- PSR-12 coding standards
-- Proper error handling (try-catch)
-- Clear variable names
-- Comments for complex logic
-- Separate concerns (config, functions, handlers)
-- Return JSON for AJAX requests
-- Use HTTP status codes
-
-Example HTML formatting:
-```
-\n\n\n \n Page\n\n\n \n\n
-```
-
-Example PHP formatting:
-```
- false, 'error' => 'Required fields missing']);\n exit;\n }}\n \n // Process form\n echo json_encode(['success' => true, 'message' => 'Form submitted']);\n}}\n?>
-```
-
-📝 CONTENT REQUIREMENTS:
-1. Write REAL, PROFESSIONAL content (not Lorem Ipsum)
-2. Create compelling headlines and descriptions
-3. Write realistic testimonials with names
-4. Generate actual blog post content
-5. Create detailed service/product descriptions
-6. Write engaging About Us content
-7. Include realistic contact information (use placeholders)
-
-🎨 DESIGN EXCELLENCE:
-- Modern, professional, visually stunning
-- Consistent spacing (8px grid system)
-- Smooth animations (0.3s transitions)
-- Hover effects on interactive elements
-- Mobile-first responsive design
-- Glassmorphism, gradients, shadows
-- Professional color palette
-- Clean typography (2-3 fonts max)
-
-🔧 TECHNICAL REQUIREMENTS:
-- Semantic HTML5 (header, nav, main, section, article, footer)
-- CSS Grid + Flexbox layouts
-- Vanilla JavaScript (ES6+)
-- Responsive breakpoints: 320px, 768px, 1024px, 1440px
-- Form validation with error messages
-- Smooth scroll behavior
-- Loading animations
-- Interactive elements (accordions, tabs, modals)
-
-📱 MUST INCLUDE SECTIONS:
-1. Hero section with CTA button
-2. Navigation (sticky on scroll, mobile menu)
-3. About/Services section
-4. Features/Benefits cards
-5. Gallery/Portfolio (if applicable)
-6. Testimonials (if applicable)
-7. Pricing tables (if applicable)
-8. Team members (if applicable)
-9. Blog posts grid (if applicable)
-10. Contact form with validation
-11. Footer with social links
-12. Back-to-top button
-
-🗄️ DATABASE (if requested):
-- Normalized MySQL schema
-- Proper indexes and foreign keys
-- Sample data (5-10 realistic entries)
-- PHP config with PDO connection
-- CREATE DATABASE statement
-- CREATE TABLE statements
-- INSERT sample data
-- Proper data types (VARCHAR, INT, TEXT, DATETIME)
-- Auto-increment primary keys
-- Timestamps (created_at, updated_at)
-
-⚡ INTERACTIVE FEATURES:
-- Smooth scroll to sections
-- Fade-in animations on scroll
-- Form validation (real-time)
-- AJAX form submission (with PHP backend)
-- Image lightbox/modal
-- Mobile hamburger menu
-- Loading spinner
-- Success/error messages
-- Accordion/FAQ
-- Tabs for content
-- Carousel/slider (if needed)
-
-🚀 PERFORMANCE:
-- Optimized CSS (no unused styles)
-- Efficient JavaScript
-- Lazy loading for images
-- Minimal HTTP requests
-- Fast animations
-
-📊 SEO & ACCESSIBILITY:
-- Proper meta tags (title, description, keywords)
-- Open Graph tags
-- Semantic HTML structure
-- Alt text for all images
-- ARIA labels where needed
-- Keyboard navigation
-- Focus indicators
-- Sufficient color contrast
-
-📋 VISTAPANEL SETUP INSTRUCTIONS (in README):
-1. Upload files via File Manager
-2. Create MySQL database in VistaPanel
-3. Import SQL file using phpMyAdmin
-4. Update config.php with database credentials
-5. Set file permissions (755 for directories, 644 for files)
-6. Test contact form
-7. Customize content and images
-
-IMPORTANT JSON RULES:
-- Return ONLY the JSON object
-- NO markdown code blocks
-- NO explanations before or after
-- Escape quotes with backslash
-- Use \n for newlines
-- Ensure valid JSON syntax
-- Do NOT return null values
-- Generate COMPLETE, FUNCTIONAL code for ALL files
-
-CREATE PROFESSIONAL, PRODUCTION-READY CODE FOR VISTAPANEL! 🚀"""
-
- # Enhanced user prompt with detailed specifications
- features_text = ', '.join(req.features) if req.features else 'Standard features'
- color_guide = _get_color_scheme_guide(req.color_scheme)
-
- user_prompt = f"""🎨 PROJECT BRIEF:
-
-📋 WEBSITE TYPE: {req.business_type.upper()}
-
-💬 CLIENT DESCRIPTION:
-{req.prompt}
-
-🎨 DESIGN SPECIFICATIONS:
-- Color Scheme: {req.color_scheme}
-- Style: Modern, professional, visually stunning
-- Layout: Clean, spacious, well-organized
-- Typography: Professional, readable, hierarchical
-
-✨ REQUIRED FEATURES:
-{features_text}
-
-🗄️ DATABASE: {'YES - Include full MySQL schema with sample data' if req.include_database else 'NO - Static website only'}
-
-🎯 SPECIFIC REQUIREMENTS:
-
-1. HERO SECTION:
- - Eye-catching headline
- - Compelling subheadline
- - Clear call-to-action button
- - Background: gradient or image
- - Smooth scroll-down indicator
-
-2. NAVIGATION:
- - Sticky header on scroll
- - Smooth scroll to sections
- - Mobile hamburger menu
- - Active section highlighting
-
-3. CONTENT SECTIONS:
- - About/Services section with icons
- - Features/Benefits with cards
- - Testimonials with star ratings (if requested)
- - Gallery with lightbox (if requested)
- - Pricing tables (if requested)
- - Team members (if requested)
- - Blog posts grid (if requested)
- - FAQ accordion (if applicable)
-
-4. CONTACT SECTION:
- - Working contact form with validation
- - Email, phone, address display
- - Social media links
- - Google Maps embed (placeholder)
-
-5. FOOTER:
- - Multi-column layout
- - Quick links
- - Social media icons
- - Copyright notice
- - Back-to-top button
-
-6. INTERACTIVE ELEMENTS:
- - Smooth scroll animations (fade-in, slide-up)
- - Hover effects on cards and buttons
- - Loading states for forms
- - Success/error messages
- - Image lazy loading
- - Parallax effects (subtle)
-
-7. FORMS (if applicable):
- - Real-time validation
- - Clear error messages
- - Success confirmation
- - Spam protection (honeypot)
- - Required field indicators
-
-8. PERFORMANCE:
- - Optimized CSS (no unused styles)
- - Efficient JavaScript (no jQuery)
- - Fast loading animations
- - Minimal HTTP requests
-
-9. SEO:
- - Proper meta tags (title, description, keywords)
- - Open Graph tags for social sharing
- - Structured data (JSON-LD)
- - Semantic HTML structure
- - Alt text for all images
-
-10. ACCESSIBILITY:
- - ARIA labels where needed
- - Keyboard navigation support
- - Focus indicators
- - Sufficient color contrast
- - Screen reader friendly
-
-{f'''11. DATABASE STRUCTURE (if applicable):
- - Users table (id, name, email, password, created_at)
- - Content table (id, title, description, image, created_at)
- - Messages/Contacts table (id, name, email, message, created_at)
- - Categories/Tags (if blog)
- - Proper relationships and indexes
- - Sample data for testing''' if req.include_database else ''}
-
-🎨 COLOR SCHEME GUIDANCE:
-{color_guide}
-
-📱 RESPONSIVE BREAKPOINTS:
-- Mobile: 320px - 767px (single column, stacked layout)
-- Tablet: 768px - 1023px (2 columns where appropriate)
-- Desktop: 1024px+ (full layout, max-width 1400px)
-
-⚡ MUST INCLUDE:
-- Favicon link (placeholder)
-- Google Fonts (1-2 professional fonts)
-- Font Awesome icons (CDN)
-- Smooth scroll behavior
-- Loading animation on page load
-- 404 error handling
-- Print stylesheet basics
-
-🚫 AVOID:
-- jQuery or heavy frameworks
-- Inline styles (except critical CSS)
-- !important in CSS (unless necessary)
-- Console.log in production code
-- Hardcoded sensitive data
-- Broken links or placeholder text
-
-📦 DELIVERABLES:
-Return ONLY valid JSON with these exact keys:
-- html: Complete HTML5 document
-- css: Complete stylesheet with comments
-- js: Complete JavaScript with error handling
-- sql: MySQL schema with sample data (if database requested)
-- config: PHP database config (if database requested)
-- readme: Detailed setup and customization guide
-
-CREATE A WEBSITE THAT WILL IMPRESS AND DELIGHT! 🚀✨"""
-
- logger.info(f"Prompt sizes - System: {len(system_prompt)}, User: {len(user_prompt)}")
-
- # Call Groq API
- headers = {
- "Authorization": f"Bearer {api_key}",
- "Content-Type": "application/json"
- }
-
- payload = {
- "model": model,
- "messages": [
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_prompt}
- ],
- "temperature": 0.7,
- "max_tokens": 8000,
- "top_p": 0.9
- }
-
- logger.info("Calling Groq API...")
- response = requests.post(
- "https://api.groq.com/openai/v1/chat/completions",
- headers=headers,
- json=payload,
- timeout=60
- )
-
- logger.info(f"Groq Response: {response.status_code}")
-
- if response.status_code != 200:
- logger.error(f"Groq Error: {response.text}")
- raise HTTPException(status_code=response.status_code, detail=response.text)
-
- result = response.json()
- raw_content = result['choices'][0]['message']['content']
- logger.info(f"Response length: {len(raw_content)} chars")
-
- # Try to extract JSON from markdown code blocks if present
- if '```json' in raw_content:
- import re
- json_match = re.search(r'```json\s*(.+?)\s*```', raw_content, re.DOTALL)
- if json_match:
- raw_content = json_match.group(1)
- logger.info("Extracted JSON from markdown block")
- elif '```' in raw_content:
- import re
- json_match = re.search(r'```\s*(.+?)\s*```', raw_content, re.DOTALL)
- if json_match:
- raw_content = json_match.group(1)
- logger.info("Extracted content from code block")
-
- logger.info(f"Preview: {raw_content[:200]}...")
-
- try:
- generated_content = json.loads(raw_content)
-
- # Validate that we have the expected structure
- if not isinstance(generated_content, dict):
- raise ValueError("Response is not a JSON object")
-
- # Check if we have the required files
- required_keys = ['html', 'css', 'js']
- has_required = any(key in generated_content for key in required_keys)
-
- if not has_required:
- # Check if the response is double-encoded (JSON string containing JSON)
- if len(generated_content) == 1:
- first_key = list(generated_content.keys())[0]
- first_value = generated_content[first_key]
- if isinstance(first_value, str) and first_value.strip().startswith('{'):
- try:
- inner_json = json.loads(first_value)
- if isinstance(inner_json, dict) and any(k in inner_json for k in required_keys):
- generated_content = inner_json
- logger.info("Unwrapped double-encoded JSON")
- except:
- pass
-
- # Final validation
- if 'html' not in generated_content:
- logger.warning("No HTML in response, using fallback")
- raise ValueError("Missing HTML content")
-
- logger.info(f"Files: {list(generated_content.keys())}")
-
- except (json.JSONDecodeError, ValueError) as je:
- logger.error(f"JSON parse failed: {str(je)}")
-
- # Try to fix common JSON issues
- try:
- # Remove markdown code blocks if still present
- if '```' in raw_content:
- parts = raw_content.split('```')
- for part in parts:
- if '{' in part and '}' in part:
- raw_content = part.replace('json\n', '').replace('json', '').strip()
- break
-
- # Try parsing again
- generated_content = json.loads(raw_content)
-
- # Validate again
- if not isinstance(generated_content, dict) or 'html' not in generated_content:
- raise ValueError("Invalid structure after cleanup")
-
- logger.info("Fixed JSON after cleanup")
- logger.info(f"Files: {list(generated_content.keys())}")
-
- except Exception as cleanup_error:
- # Final fallback: create minimal structure only if we have HTML-like content
- logger.warning(f"Using fallback structure: {str(cleanup_error)}")
-
- if ' str:
- """Return detailed color guidance based on selected scheme"""
- schemes = {
- "modern": """Primary: #6366f1 (Indigo), Secondary: #8b5cf6 (Purple)
-Accent: #ec4899 (Pink), Background: #0f172a (Dark Blue)
-Text: #f1f5f9 (Light Gray), Use gradients and glassmorphism""",
- "dark": """Primary: #3b82f6 (Blue), Secondary: #10b981 (Green)
-Accent: #f59e0b (Amber), Background: #111827 (Very Dark)
-Text: #f9fafb (White), High contrast, neon accents""",
- "light": """Primary: #2563eb (Blue), Secondary: #7c3aed (Purple)
-Accent: #dc2626 (Red), Background: #ffffff (White)
-Text: #1f2937 (Dark Gray), Clean, minimal, lots of whitespace""",
- "vibrant": """Primary: #f43f5e (Rose), Secondary: #8b5cf6 (Purple)
-Accent: #f59e0b (Amber), Background: #1e293b (Dark Slate)
-Text: #f1f5f9 (Light), Bold colors, high energy""",
- "professional": """Primary: #1e40af (Navy), Secondary: #475569 (Slate)
-Accent: #0891b2 (Cyan), Background: #f8fafc (Off White)
-Text: #0f172a (Dark), Corporate, trustworthy""",
- "warm": """Primary: #ea580c (Orange), Secondary: #dc2626 (Red)
-Accent: #facc15 (Yellow), Background: #292524 (Warm Dark)
-Text: #fafaf9 (Warm White), Cozy, inviting""",
- "nature": """Primary: #16a34a (Green), Secondary: #65a30d (Lime)
-Accent: #0891b2 (Teal), Background: #1c1917 (Earth Dark)
-Text: #fafaf9 (Natural White), Organic, earthy"""
- }
- return schemes.get(color_scheme, schemes["modern"])
-
-@app.post("/create")
-def create_account(req: AccountRequest):
- """Create MOFH hosting account"""
- REQUEST_COUNT["total"] += 1
- REQUEST_COUNT["create"] += 1
-
- try:
- # Use HTTP Basic Authentication (the correct method)
- auth_string = f"{MOFH_API_USERNAME}:{MOFH_API_PASSWORD}"
- auth_b64 = base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')
-
- headers = {
- 'Authorization': f'Basic {auth_b64}',
- 'Content-Type': 'application/x-www-form-urlencoded'
- }
-
- # Data WITHOUT api_user/api_key (they're in the header)
- api_data = {
- 'username': req.username,
- 'password': req.password,
- 'contactemail': req.email,
- 'domain': req.domain,
- 'plan': req.plan
- }
-
- response = requests.post(
- f"{MOFH_API_URL}createacct.php",
- data=api_data,
- headers=headers,
- timeout=30,
- verify=False
- )
-
- return {
- 'success': response.status_code == 200,
- 'http_code': response.status_code,
- 'response': response.text,
- 'debug': {
- 'api_url': f"{MOFH_API_URL}createacct.php",
- 'auth_method': 'Basic Auth (HTTP Header)',
- 'api_user_length': len(MOFH_API_USERNAME),
- 'api_key_length': len(MOFH_API_PASSWORD),
- 'sent_params': list(api_data.keys())
- }
- }
-
- except Exception as e:
- REQUEST_COUNT["errors"] += 1
- raise HTTPException(status_code=500, detail=str(e))
-
-class FTPDeployRequest(BaseModel):
- ftp_host: str
- ftp_user: str
- ftp_pass: str
- files: dict # {"index.html": "content", "style.css": "content", ...}
-
- @validator('ftp_host')
- def validate_host(cls, v):
- if not v or len(v) < 3:
- raise ValueError('Invalid FTP host')
- return v.strip()
-
-@app.post("/deploy-ftp")
-def deploy_via_ftp(req: FTPDeployRequest):
- """Deploy files to FTP server (HF Space has no firewall restrictions)"""
- REQUEST_COUNT["total"] += 1
-
- try:
- import ftplib
- from io import BytesIO
-
- logger.info(f"[FTP-DEPLOY] Connecting to {req.ftp_host}")
-
- deployed_files = []
- errors = []
-
- # Try regular FTP first
- ftp = None
- try:
- ftp = ftplib.FTP(timeout=30)
- ftp.connect(req.ftp_host, 21)
- ftp.login(req.ftp_user, req.ftp_pass)
- logger.info("[FTP-DEPLOY] Connected via regular FTP")
- except Exception as e:
- logger.warning(f"[FTP-DEPLOY] Regular FTP failed: {e}")
- # Try FTP_TLS
- try:
- ftp = ftplib.FTP_TLS(timeout=30)
- ftp.connect(req.ftp_host, 21)
- ftp.login(req.ftp_user, req.ftp_pass)
- ftp.prot_p() # Enable encryption
- logger.info("[FTP-DEPLOY] Connected via FTP_TLS")
- except Exception as e2:
- logger.error(f"[FTP-DEPLOY] FTP_TLS also failed: {e2}")
- raise HTTPException(status_code=500, detail=f"FTP connection failed: {str(e2)}")
-
- # Set passive mode
- ftp.set_pasv(True)
-
- # Try to change to web directory
- web_dirs = ['htdocs', 'public_html', 'www', 'html']
- current_dir = '/'
-
- for web_dir in web_dirs:
- try:
- ftp.cwd(web_dir)
- current_dir = ftp.pwd()
- logger.info(f"[FTP-DEPLOY] Changed to directory: {current_dir}")
- break
- except:
- continue
-
- logger.info(f"[FTP-DEPLOY] Working directory: {current_dir}")
-
- # Upload each file
- for filename, content in req.files.items():
- try:
- logger.info(f"[FTP-DEPLOY] Uploading {filename} ({len(content)} bytes)")
-
- # Convert string to bytes
- file_bytes = BytesIO(content.encode('utf-8'))
-
- # Upload file
- ftp.storbinary(f'STOR {filename}', file_bytes)
-
- deployed_files.append(filename)
- logger.info(f"[FTP-DEPLOY] ✓ {filename} uploaded")
-
- except Exception as e:
- error_msg = f"{filename}: {str(e)}"
- errors.append(error_msg)
- logger.error(f"[FTP-DEPLOY] ✗ {error_msg}")
-
- # Close FTP connection
- try:
- ftp.quit()
- except:
- ftp.close()
-
- logger.info(f"[FTP-DEPLOY] Deployment complete. Success: {len(deployed_files)}, Errors: {len(errors)}")
-
- if not deployed_files:
- raise HTTPException(status_code=500, detail=f"No files deployed. Errors: {', '.join(errors)}")
-
- return {
- "success": True,
- "deployed_files": deployed_files,
- "errors": errors,
- "message": f"Successfully deployed {len(deployed_files)} file(s)"
- }
-
- except HTTPException:
- raise
- except Exception as e:
- REQUEST_COUNT["errors"] += 1
- logger.error(f"[FTP-DEPLOY] Error: {str(e)}")
- raise HTTPException(status_code=500, detail=f"Deployment failed: {str(e)}")
-
-
-class GenerateAndDeployRequest(BaseModel):
- prompt: str
- business_type: Optional[str] = "general"
- color_scheme: Optional[str] = "modern"
- include_database: Optional[bool] = False
- features: Optional[List[str]] = []
- ftp_host: str
- ftp_user: str
- ftp_pass: str
-
- @validator('prompt')
- def validate_prompt(cls, v):
- if not v or len(v.strip()) < 10:
- raise ValueError('Prompt must be at least 10 characters')
- if len(v) > 10000:
- raise ValueError('Prompt too long (max 10000 characters)')
- return v.strip()
-
-@app.post("/generate-and-deploy")
-def generate_and_deploy(req: GenerateAndDeployRequest):
- """Generate website with AI and deploy to FTP in one call"""
- REQUEST_COUNT["total"] += 1
- REQUEST_COUNT["generate"] += 1
-
- logs = []
-
- try:
- import ftplib
- from io import BytesIO
-
- logs.append({"time": time.time(), "type": "info", "message": "Starting AI generation..."})
-
- logger.info("=== IONA AI Generation + Deployment Started ===")
- logger.info(f"Prompt: {req.prompt[:100]}...")
- logger.info(f"FTP Host: {req.ftp_host}, User: {req.ftp_user}")
-
- api_key = get_next_api_key()
- model = get_random_model()
-
- logs.append({"time": time.time(), "type": "info", "message": f"Using model: {model}"})
-
- # Generate with AI - FORCE JSON MODE WITH DETAILED INSTRUCTIONS
- system_prompt = """You are a web development code generator.
-
-Generate a complete, functional website with HTML, CSS, and JavaScript.
-
-Return a JSON object with these keys:
-- html: A complete HTML5 document with proper structure
-- css: Complete CSS stylesheet with modern styling
-- js: JavaScript code for interactivity
-- readme: Brief setup instructions
-
-The website must be:
-- Fully functional and complete
-- Mobile responsive
-- Modern and professional
-- Include all requested features
-
-IMPORTANT CODE FORMATTING RULES:
-- Use proper indentation (2 spaces per level)
-- Add newlines between sections
-- Format code readably with line breaks
-- Do NOT put everything on one line
-- Use \n for newlines in JSON strings
-- Properly indent nested elements
-
-IMPORTANT: Do NOT return null values. Generate actual code for each field."""
-
- user_prompt = f"""Generate a {req.business_type} website with this description:
-
-{req.prompt}
-
-Use {req.color_scheme} color scheme and include {', '.join(req.features) if req.features else 'standard'} features.
-
-Return as JSON with html, css, js, and readme keys."""
-
- headers = {
- "Authorization": f"Bearer {api_key}",
- "Content-Type": "application/json"
- }
-
- payload = {
- "model": model,
- "messages": [
- {"role": "system", "content": system_prompt},
- {"role": "user", "content": user_prompt}
- ],
- "temperature": 0.7, # Balanced for creativity and consistency
- "max_tokens": 32000, # Maximum tokens for complete generation
- "response_format": {"type": "json_object"} # Force JSON mode
- }
-
- logs.append({"time": time.time(), "type": "info", "message": "Calling Groq AI..."})
-
- response = requests.post(
- "https://api.groq.com/openai/v1/chat/completions",
- headers=headers,
- json=payload,
- timeout=180 # 3 minutes for complex generation
- )
-
- if response.status_code != 200:
- logs.append({"time": time.time(), "type": "error", "message": f"Groq API error: HTTP {response.status_code}"})
- raise HTTPException(status_code=response.status_code, detail=response.text)
-
- result = response.json()
- raw_content = result['choices'][0]['message']['content']
-
- logger.info(f"[GENERATE-DEPLOY] Raw response length: {len(raw_content)}")
- logger.info(f"[GENERATE-DEPLOY] Raw response preview: {raw_content[:200]}")
-
- logs.append({"time": time.time(), "type": "success", "message": "AI generation complete"})
- logs.append({"time": time.time(), "type": "info", "message": "Parsing AI response..."})
-
- # Parse JSON with better error handling
- generated_content = None
-
- # Try extracting from markdown code blocks
- if '```json' in raw_content:
- import re
- json_match = re.search(r'```json\s*(.+?)\s*```', raw_content, re.DOTALL)
- if json_match:
- raw_content = json_match.group(1)
- logger.info("[GENERATE-DEPLOY] Extracted from ```json block")
- elif '```' in raw_content:
- import re
- json_match = re.search(r'```\s*(.+?)\s*```', raw_content, re.DOTALL)
- if json_match:
- raw_content = json_match.group(1)
- logger.info("[GENERATE-DEPLOY] Extracted from ``` block")
-
- # Try parsing JSON
- try:
- generated_content = json.loads(raw_content)
- logger.info(f"[GENERATE-DEPLOY] Parsed JSON successfully, keys: {list(generated_content.keys())}")
- except json.JSONDecodeError as e:
- logger.error(f"[GENERATE-DEPLOY] JSON parse failed: {e}")
- logger.error(f"[GENERATE-DEPLOY] Content: {raw_content[:500]}")
-
- # Try to find JSON object in the response
- import re
- json_pattern = r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}'
- matches = re.findall(json_pattern, raw_content, re.DOTALL)
-
- for match in matches:
- try:
- generated_content = json.loads(match)
- if 'html' in generated_content:
- logger.info("[GENERATE-DEPLOY] Found valid JSON in response")
- break
- except:
- continue
-
- if not generated_content:
- logs.append({"time": time.time(), "type": "error", "message": "AI returned invalid JSON format"})
- raise ValueError(f"AI response is not valid JSON. Error: {str(e)}. Response preview: {raw_content[:200]}")
-
- if 'html' not in generated_content:
- logs.append({"time": time.time(), "type": "error", "message": "No HTML in AI response"})
- raise ValueError("No HTML in AI response")
-
- # Check if HTML is null or empty
- html_content = generated_content.get('html', '')
- if not html_content or html_content == 'null' or len(html_content.strip()) < 100:
- logs.append({"time": time.time(), "type": "error", "message": f"AI returned invalid HTML (length: {len(html_content)})"})
- logs.append({"time": time.time(), "type": "error", "message": f"Model used: {model}"})
- logs.append({"time": time.time(), "type": "error", "message": f"Response preview: {str(generated_content)[:200]}"})
- raise ValueError(f"AI returned null or empty HTML content. Model: {model}. Please try again.")
-
- logs.append({"time": time.time(), "type": "success", "message": "AI response parsed successfully"})
- logs.append({"time": time.time(), "type": "info", "message": f"Generated {len(generated_content.get('html', ''))} chars of HTML"})
-
- logs.append({"time": time.time(), "type": "info", "message": "FTP blocked - returning files to PHP for deployment"})
-
- # HF Space cannot connect to FTP (port 21 blocked)
- # Return generated files to PHP for deployment
- logger.info("[GENERATE-DEPLOY] FTP blocked by HF network - returning files")
-
- return {
- "success": True,
- "model_used": model,
- "deployed_files": [],
- "files": {
- 'html': generated_content.get('html', ''),
- 'css': generated_content.get('css', ''),
- 'js': generated_content.get('js', ''),
- 'php': generated_content.get('php', ''),
- 'sql': generated_content.get('sql', ''),
- 'config': generated_content.get('config', ''),
- 'readme': generated_content.get('readme', '')
- },
- "ftp_blocked": True,
- "logs": logs,
- "message": "Website generated successfully. FTP blocked - files returned for PHP deployment."
- }
-
- except HTTPException:
- raise
- except json.JSONDecodeError as e:
- REQUEST_COUNT["errors"] += 1
- logger.error(f"[GENERATE-DEPLOY] JSON Error: {str(e)}")
- logs.append({"time": time.time(), "type": "error", "message": f"JSON parsing failed: {str(e)}"})
- raise HTTPException(status_code=500, detail=f"AI returned invalid JSON format. Please try again. Error: {str(e)}")
- except Exception as e:
- REQUEST_COUNT["errors"] += 1
- logger.error(f"[GENERATE-DEPLOY] Error: {str(e)}")
- logs.append({"time": time.time(), "type": "error", "message": str(e)})
- raise HTTPException(status_code=500, detail=f"Generation/Deployment failed: {str(e)}")
+from fastapi import FastAPI, HTTPException, Response
+from fastapi.responses import HTMLResponse
+from pydantic import BaseModel, validator
+import requests
+import os
+import base64
+import json
+from typing import Optional, List
+import random
+import time
+from datetime import datetime
+import psutil
+import logging
+
+# Configure logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+app = FastAPI(title="MOFH API Proxy + IONA AI")
+
+# Track uptime
+START_TIME = time.time()
+REQUEST_COUNT = {"total": 0, "generate": 0, "create": 0, "errors": 0}
+
+# High-quality image library for professional websites
+WEBSITE_IMAGES = [
+ "https://picsum.photos/id/1/1920/1080",
+ "https://picsum.photos/id/2/1920/1080",
+ "https://picsum.photos/id/3/1920/1080",
+ "https://picsum.photos/id/4/1920/1080",
+ "https://picsum.photos/id/5/1920/1080",
+ "https://picsum.photos/id/6/1920/1080",
+ "https://picsum.photos/id/7/1920/1080",
+ "https://picsum.photos/id/8/1920/1080",
+ "https://picsum.photos/id/9/1920/1080",
+ "https://picsum.photos/id/36/1920/1080",
+ "https://picsum.photos/id/37/1920/1080",
+ "https://picsum.photos/id/38/1920/1080",
+ "https://picsum.photos/id/39/1920/1080",
+ "https://picsum.photos/id/40/1920/1080",
+ "https://picsum.photos/id/41/1920/1080",
+ "https://picsum.photos/id/42/1920/1080",
+ "https://picsum.photos/id/43/1920/1080",
+ "https://picsum.photos/id/44/1920/1080",
+ "https://picsum.photos/id/45/1920/1080",
+ "https://picsum.photos/id/46/1920/1080",
+ "https://picsum.photos/id/47/1920/1080",
+ "https://picsum.photos/id/48/1920/1080",
+ "https://picsum.photos/id/49/1920/1080",
+ "https://picsum.photos/id/50/1920/1080",
+ "https://picsum.photos/id/51/1920/1080",
+ "https://picsum.photos/id/52/1920/1080",
+ "https://picsum.photos/id/53/1920/1080",
+ "https://picsum.photos/id/54/1920/1080",
+ "https://picsum.photos/id/55/1920/1080",
+ "https://picsum.photos/id/56/1920/1080",
+ "https://picsum.photos/id/57/1920/1080",
+ "https://picsum.photos/id/58/1920/1080",
+ "https://picsum.photos/id/59/1920/1080",
+ "https://picsum.photos/id/60/1920/1080",
+ "https://images.unsplash.com/photo-1497366754035-f200968a6e72?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1497366811353-6870744d04b2?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1497215728101-856f4ea42174?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1441986300917-64674bd600d8?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1504384308090-c894fdcc538d?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1519389950473-47ba0277781c?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1542744094-24638eff58bb?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1460925895917-afdab827c52f?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1552664730-d307ca884978?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1531482615713-2afd69097998?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&q=80&w=1920&h=1080",
+ "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&q=80&w=1920&h=1080"
+]
+
+def get_random_images(count=10):
+ """Get random images from the library"""
+ return random.sample(WEBSITE_IMAGES, min(count, len(WEBSITE_IMAGES)))
+
+MOFH_API_URL = "https://panel.myownfreehost.net:2087/xml-api/"
+MOFH_API_USERNAME = "ZqHK1H5BBnrrywnsXXW2pY42QNqDxHru74HtwZofkn1LRawd4TDcKCPnIViBiPaJg1sUXUlKErn4EhXmqu0vx1STF263TxiTQuyKzYcErjSax5W4lBoLXqpE0aQqnLLKi8Pk7fncTbcfwqnKVRNOzLd34xdakdiHH1Z4gmDbetSmYauCId4Cj8SVizSBTuN6TMIrz2oMKAoXPypSocVOOUI0rSOIld34MY6yXtcHdSgh3Jd5J8mgM8SiW8BvHtE"
+MOFH_API_PASSWORD = "Uj39SyF5okKP73Ml6laLEay40MZEXSe81lStBEc9dQC2odK49Tw4K5l4BXI3DfnIVA5ZoIH0GFKvBRBO1cLIU1djjSBCABBkp2GO2oeH4BROw238dZwODu0kklR0uezHPfezHMCbcbjSKLRKJX5Ns7gZ9wgXxZbO3e8Rf4MoZnQVk5IUNLvKPWHDP43PsQnAFAYOhDNnKPBBg2CoHXsJ1Kq8C5wRuBd64GPyPhY7sbSCibAMUWKRkUSzRi7aq5i"
+
+# IONA AI - Multiple API Keys for Load Balancing (No Rate Limits)
+GROQ_API_KEYS = [
+ "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", # Replace with real keys
+ "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",
+ "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",
+ "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"
+]
+
+# Model Configuration - Use ONLY the most capable model
+AI_MODELS = {
+ "groq": [
+ "llama-3.3-70b-versatile", # Most capable, reliable for complex generation
+ ]
+}
+
+# Track API usage for rotation
+api_usage_counter = {key: 0 for key in GROQ_API_KEYS}
+
+def get_next_api_key():
+ """Round-robin API key selection for load balancing"""
+ min_usage_key = min(api_usage_counter, key=api_usage_counter.get)
+ api_usage_counter[min_usage_key] += 1
+ return min_usage_key
+
+def get_random_model():
+ """Select model (always use 70B for reliability)"""
+ return AI_MODELS["groq"][0] # Always use llama-3.3-70b-versatile
+
+class AIWebsiteRequest(BaseModel):
+ prompt: str
+ business_type: Optional[str] = "general"
+ color_scheme: Optional[str] = "modern"
+ include_database: Optional[bool] = True
+ php_only: Optional[bool] = False
+ features: Optional[List[str]] = []
+
+ @validator('prompt')
+ def validate_prompt(cls, v):
+ if not v or len(v.strip()) < 10:
+ raise ValueError('Prompt must be at least 10 characters')
+ if len(v) > 10000:
+ raise ValueError('Prompt too long (max 10000 characters)')
+ return v.strip()
+
+class AccountRequest(BaseModel):
+ username: str
+ password: str
+ email: str
+ domain: str
+ plan: str = "free"
+
+ @validator('username')
+ def validate_username(cls, v):
+ if not v or len(v) < 3:
+ raise ValueError('Username must be at least 3 characters')
+ if len(v) > 20:
+ raise ValueError('Username too long (max 20 characters)')
+ return v.strip()
+
+ @validator('email')
+ def validate_email(cls, v):
+ if '@' not in v or '.' not in v:
+ raise ValueError('Invalid email format')
+ return v.strip()
+
+@app.get("/", response_class=HTMLResponse)
+def root():
+ """Uptime dashboard with external IP display"""
+ import socket
+
+ hostname = socket.gethostname()
+ local_ip = socket.gethostbyname(hostname)
+
+ # Get external IP
+ try:
+ external_ip = requests.get('https://api.ipify.org', timeout=5).text
+ except:
+ external_ip = "Unable to detect"
+
+ # Calculate uptime
+ uptime_seconds = int(time.time() - START_TIME)
+ uptime_hours = uptime_seconds // 3600
+ uptime_minutes = (uptime_seconds % 3600) // 60
+ uptime_secs = uptime_seconds % 60
+
+ # Get system stats
+ try:
+ cpu_percent = psutil.cpu_percent(interval=1)
+ memory = psutil.virtual_memory()
+ memory_percent = memory.percent
+ memory_used = memory.used / (1024**3) # GB
+ memory_total = memory.total / (1024**3) # GB
+ except:
+ cpu_percent = 0
+ memory_percent = 0
+ memory_used = 0
+ memory_total = 0
+
+ # API key status
+ api_key_usage = []
+ for key, count in api_usage_counter.items():
+ api_key_usage.append(f"{key[:20]}... → {count} requests")
+
+ html_content = f"""
+
+
+
+
+
+ IONA AI - System Status
+
+
+
+
+
+
+
+
+
⏱️ Uptime
+
+ Running Since
+ {datetime.fromtimestamp(START_TIME).strftime('%Y-%m-%d %H:%M:%S')}
+
+
+ Uptime
+ {uptime_hours}h {uptime_minutes}m {uptime_secs}s
+
+
+ Total Requests
+ {REQUEST_COUNT['total']}
+
+
+ Errors
+ {REQUEST_COUNT['errors']}
+
+
+
+
+
🌐 Network Info
+
+ Hostname
+ {hostname}
+
+
+ Local IP
+ {local_ip}
+
+
+
External IP Address
+
{external_ip}
+
+
+ ⚠️ Add this IP to MOFH API whitelist
+
+
+
+
+
💻 System Resources
+
+ CPU Usage
+ {cpu_percent:.1f}%
+
+
+
+ Memory Usage
+ {memory_percent:.1f}%
+
+
+
{memory_percent:.1f}%
+
+
+ Memory
+ {memory_used:.2f} GB / {memory_total:.2f} GB
+
+
+
+
+
+
+
🤖 AI Configuration
+
+ Active Models
+ {len(AI_MODELS['groq'])}
+
+
+ API Keys
+ {len(GROQ_API_KEYS)}
+
+
+ Generations
+ {REQUEST_COUNT['generate']}
+
+
+ {''.join([f'
{usage}
' for usage in api_key_usage])}
+
+
+
+
+
+
+
👨💻 Developer Info
+
+ Developer
+ Pratyush Srivastava
+
+
+
+
+ Version
+ 2026.1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
+
+ return html_content
+
+@app.get("/health")
+def health_check():
+ """Health check endpoint for monitoring"""
+ return {
+ "status": "healthy",
+ "uptime_seconds": int(time.time() - START_TIME),
+ "timestamp": datetime.now().isoformat(),
+ "requests": REQUEST_COUNT
+ }
+
+@app.get("/test")
+def test_generation():
+ """Test AI generation with a simple prompt"""
+ try:
+ api_key = get_next_api_key()
+ model = get_random_model()
+
+ test_prompt = "Create a simple HTML page with a heading 'Hello World' and a paragraph."
+
+ headers = {
+ "Authorization": f"Bearer {api_key}",
+ "Content-Type": "application/json"
+ }
+
+ payload = {
+ "model": model,
+ "messages": [
+ {"role": "system", "content": "You are a helpful assistant. Return only valid JSON."},
+ {"role": "user", "content": test_prompt}
+ ],
+ "temperature": 0.7,
+ "max_tokens": 500
+ }
+
+ response = requests.post(
+ "https://api.groq.com/openai/v1/chat/completions",
+ headers=headers,
+ json=payload,
+ timeout=30
+ )
+
+ if response.status_code == 200:
+ result = response.json()
+ return {
+ "success": True,
+ "model": model,
+ "response_length": len(result['choices'][0]['message']['content']),
+ "tokens_used": result.get('usage', {}),
+ "message": "AI is working correctly!"
+ }
+ else:
+ return {
+ "success": False,
+ "error": f"HTTP {response.status_code}",
+ "message": "AI test failed"
+ }
+ except Exception as e:
+ return {
+ "success": False,
+ "error": str(e),
+ "message": "AI test failed with exception"
+ }
+
+@app.post("/generate-website")
+def generate_website(req: AIWebsiteRequest):
+ """IONA AI - Generate complete website with HTML, CSS, JS, and MySQL"""
+ REQUEST_COUNT["total"] += 1
+ REQUEST_COUNT["generate"] += 1
+
+ try:
+ logger.info("=== IONA AI Generation Started ===")
+ logger.info(f"Business Type: {req.business_type}")
+ logger.info(f"Prompt Length: {len(req.prompt)} chars")
+ logger.info(f"PHP Only Mode: {req.php_only}")
+
+ api_key = get_next_api_key()
+ model = get_random_model()
+ logger.info(f"Model: {model}, API Key: {api_key[:20]}...")
+
+ # Get random images for this website
+ selected_images = get_random_images(15)
+ images_list = '\n'.join([f"- {img}" for img in selected_images])
+
+ # Check if PHP-only mode
+ if req.php_only:
+ # PHP-only generation prompt
+ system_prompt = f"""You are IONA AI, an expert PHP backend developer.
+
+🎯 HOSTING ENVIRONMENT: VistaPanel (Free Hosting)
+- PHP Version: 7.4 or 8.0
+- MySQL Database available
+- PDO and MySQLi supported
+
+🎯 MISSION: Generate ONLY PHP BACKEND FILES (NO HTML/CSS/JS).
+
+📦 REQUIRED OUTPUT FORMAT:
+Return ONLY a valid JSON object with these keys:
+{{
+ "contact_php": "Complete contact form handler with validation and email sending",
+ "api_php": "RESTful API endpoints with JSON responses",
+ "functions_php": "Helper functions for database, validation, sanitization",
+ "config": "Database configuration with PDO connection",
+ "sql": "MySQL schema with sample data",
+ "readme": "Setup guide for VistaPanel"
+}}
+
+🐘 PHP FILE REQUIREMENTS:
+
+1. **contact_php** (Contact Form Handler):
+ - Handle POST requests
+ - Validate: name, email, phone, message
+ - Sanitize all inputs (htmlspecialchars, filter_var)
+ - Send email using mail() function
+ - Store in database
+ - Return JSON response
+ - CSRF protection
+ - Rate limiting (session-based)
+ - Error handling
+
+2. **api_php** (API Endpoints):
+ - RESTful structure
+ - GET /api.php?action=list (list items)
+ - POST /api.php?action=create (create item)
+ - PUT /api.php?action=update (update item)
+ - DELETE /api.php?action=delete (delete item)
+ - JSON responses
+ - Authentication (if needed)
+ - Input validation
+ - Error handling
+
+3. **functions_php** (Helper Functions):
+ - Database query helpers (select, insert, update, delete)
+ - Input validation functions
+ - Sanitization functions
+ - Email sending function
+ - Authentication helpers
+ - Session management
+ - Error logging
+
+4. **config** (Database Configuration):
+ - PDO connection setup
+ - Database credentials (localhost, username, password, dbname)
+ - Error handling (try-catch)
+ - Timezone settings
+ - Error reporting settings
+ - Session configuration
+
+5. **sql** (MySQL Schema):
+ - CREATE DATABASE statement
+ - CREATE TABLE statements
+ - Proper data types (VARCHAR, INT, TEXT, DATETIME)
+ - Primary keys (AUTO_INCREMENT)
+ - Foreign keys and indexes
+ - Timestamps (created_at, updated_at)
+ - Sample data (5-10 realistic entries)
+
+PHP SECURITY REQUIREMENTS:
+- Use prepared statements (PDO) for ALL database queries
+- Sanitize ALL user inputs
+- Validate email addresses with filter_var()
+- Use password_hash() for passwords
+- Implement CSRF tokens
+- Use htmlspecialchars() for output
+- Set proper error reporting
+- Use sessions securely
+
+PHP BEST PRACTICES:
+- PSR-12 coding standards
+- Proper indentation (4 spaces)
+- Clear variable names
+- Comments for complex logic
+- Separate concerns
+- Return JSON for AJAX requests
+- Use HTTP status codes
+- Error handling (try-catch)
+
+CODE FORMATTING:
+- Proper indentation (4 spaces per level)
+- Blank lines between functions
+- Comments for each function
+- Use \n for newlines in JSON strings
+
+IMPORTANT JSON RULES:
+- Return ONLY the JSON object
+- NO markdown code blocks
+- NO explanations
+- Escape quotes with backslash
+- Use \n for newlines
+- Do NOT return null values
+- Generate COMPLETE, FUNCTIONAL code
+
+CREATE PROFESSIONAL PHP BACKEND FOR VISTAPANEL! 🚀"""
+
+ user_prompt = f"""Generate PHP backend files for: {req.prompt}
+
+Requirements:
+- Complete contact form handler
+- RESTful API endpoints
+- Helper functions
+- Database configuration
+- MySQL schema with sample data
+
+Return as JSON with contact_php, api_php, functions_php, config, sql, and readme keys."""
+ else:
+ # Full website generation prompt
+ system_prompt = f"""You are IONA AI, an elite full-stack web developer with 15+ years of experience.
+
+🎯 HOSTING ENVIRONMENT: VistaPanel (Free Hosting)
+- PHP Version: 7.4 or 8.0
+- MySQL Database available
+- File Manager: VistaPanel
+- No SSH access
+- Standard PHP functions available
+- PDO and MySQLi supported
+
+🎯 MISSION: Generate COMPLETE, PRODUCTION-READY, MULTI-FILE websites with REAL CONTENT.
+
+📦 REQUIRED OUTPUT FORMAT:
+Return ONLY a valid JSON object with these keys:
+{{
+ "html": "Complete HTML5 document with PROPER INDENTATION (2 spaces per level)",
+ "css": "Complete CSS with PROPER FORMATTING and line breaks",
+ "js": "Complete JavaScript with PROPER FORMATTING",
+ "php": "PHP backend files (contact.php, api.php, etc.) with PROPER FORMATTING",
+ "sql": "MySQL schema with sample data (if database needed)",
+ "config": "PHP config file with database connection (if database needed)",
+ "readme": "Setup and customization guide for VistaPanel"
+}}
+
+🖼️ USE THESE REAL IMAGES (randomly selected for this website):
+{images_list}
+
+IMPORTANT IMAGE USAGE:
+- Use different images for hero, gallery, team, testimonials, blog posts
+- Add proper alt text describing each image
+- Use responsive image techniques (srcset if needed)
+- Images are high-quality 1920x1080, perfect for hero sections
+
+✨ CODE FORMATTING RULES (CRITICAL):
+1. HTML: Proper indentation with 2 spaces per level
+2. CSS: One rule per line, organized by sections with comments
+3. JavaScript: Proper function formatting with line breaks
+4. PHP: PSR-12 coding standards, proper indentation
+5. NO MINIFIED CODE - Make it readable and maintainable
+6. Add blank lines between major sections
+7. Use \n for newlines in JSON strings
+8. Properly indent nested elements
+
+🐘 PHP FILE GENERATION (IMPORTANT):
+When generating PHP files, create COMPLETE, FUNCTIONAL code:
+
+1. **contact.php** (Contact Form Handler):
+ - Validate all inputs (name, email, message)
+ - Sanitize data (htmlspecialchars, filter_var)
+ - Send email using mail() function
+ - Store in database if requested
+ - Return JSON response
+ - Include CSRF protection
+ - Rate limiting (session-based)
+
+2. **config.php** (Database Configuration):
+ - PDO connection setup
+ - Error handling (try-catch)
+ - Database credentials (localhost, username, password, dbname)
+ - Timezone settings
+ - Error reporting settings
+ - Session configuration
+
+3. **functions.php** (Helper Functions):
+ - Database query helpers
+ - Input validation functions
+ - Sanitization functions
+ - Email sending function
+ - Authentication helpers (if needed)
+
+4. **api.php** (API Endpoints):
+ - RESTful API structure
+ - JSON responses
+ - Error handling
+ - Input validation
+ - CORS headers (if needed)
+
+PHP SECURITY REQUIREMENTS:
+- Use prepared statements (PDO) for all database queries
+- Sanitize ALL user inputs
+- Validate email addresses with filter_var()
+- Use password_hash() for passwords
+- Implement CSRF tokens
+- Use htmlspecialchars() for output
+- Set proper error reporting
+- Use sessions securely
+
+PHP BEST PRACTICES:
+- PSR-12 coding standards
+- Proper error handling (try-catch)
+- Clear variable names
+- Comments for complex logic
+- Separate concerns (config, functions, handlers)
+- Return JSON for AJAX requests
+- Use HTTP status codes
+
+Example HTML formatting:
+```
+\n\n\n \n Page\n\n\n \n\n
+```
+
+Example PHP formatting:
+```
+ false, 'error' => 'Required fields missing']);\n exit;\n }}\n \n // Process form\n echo json_encode(['success' => true, 'message' => 'Form submitted']);\n}}\n?>
+```
+
+📝 CONTENT REQUIREMENTS:
+1. Write REAL, PROFESSIONAL content (not Lorem Ipsum)
+2. Create compelling headlines and descriptions
+3. Write realistic testimonials with names
+4. Generate actual blog post content
+5. Create detailed service/product descriptions
+6. Write engaging About Us content
+7. Include realistic contact information (use placeholders)
+
+🎨 DESIGN EXCELLENCE:
+- Modern, professional, visually stunning
+- Consistent spacing (8px grid system)
+- Smooth animations (0.3s transitions)
+- Hover effects on interactive elements
+- Mobile-first responsive design
+- Glassmorphism, gradients, shadows
+- Professional color palette
+- Clean typography (2-3 fonts max)
+
+🔧 TECHNICAL REQUIREMENTS:
+- Semantic HTML5 (header, nav, main, section, article, footer)
+- CSS Grid + Flexbox layouts
+- Vanilla JavaScript (ES6+)
+- Responsive breakpoints: 320px, 768px, 1024px, 1440px
+- Form validation with error messages
+- Smooth scroll behavior
+- Loading animations
+- Interactive elements (accordions, tabs, modals)
+
+📱 MUST INCLUDE SECTIONS:
+1. Hero section with CTA button
+2. Navigation (sticky on scroll, mobile menu)
+3. About/Services section
+4. Features/Benefits cards
+5. Gallery/Portfolio (if applicable)
+6. Testimonials (if applicable)
+7. Pricing tables (if applicable)
+8. Team members (if applicable)
+9. Blog posts grid (if applicable)
+10. Contact form with validation
+11. Footer with social links
+12. Back-to-top button
+
+🗄️ DATABASE (if requested):
+- Normalized MySQL schema
+- Proper indexes and foreign keys
+- Sample data (5-10 realistic entries)
+- PHP config with PDO connection
+- CREATE DATABASE statement
+- CREATE TABLE statements
+- INSERT sample data
+- Proper data types (VARCHAR, INT, TEXT, DATETIME)
+- Auto-increment primary keys
+- Timestamps (created_at, updated_at)
+
+⚡ INTERACTIVE FEATURES:
+- Smooth scroll to sections
+- Fade-in animations on scroll
+- Form validation (real-time)
+- AJAX form submission (with PHP backend)
+- Image lightbox/modal
+- Mobile hamburger menu
+- Loading spinner
+- Success/error messages
+- Accordion/FAQ
+- Tabs for content
+- Carousel/slider (if needed)
+
+🚀 PERFORMANCE:
+- Optimized CSS (no unused styles)
+- Efficient JavaScript
+- Lazy loading for images
+- Minimal HTTP requests
+- Fast animations
+
+📊 SEO & ACCESSIBILITY:
+- Proper meta tags (title, description, keywords)
+- Open Graph tags
+- Semantic HTML structure
+- Alt text for all images
+- ARIA labels where needed
+- Keyboard navigation
+- Focus indicators
+- Sufficient color contrast
+
+📋 VISTAPANEL SETUP INSTRUCTIONS (in README):
+1. Upload files via File Manager
+2. Create MySQL database in VistaPanel
+3. Import SQL file using phpMyAdmin
+4. Update config.php with database credentials
+5. Set file permissions (755 for directories, 644 for files)
+6. Test contact form
+7. Customize content and images
+
+IMPORTANT JSON RULES:
+- Return ONLY the JSON object
+- NO markdown code blocks
+- NO explanations before or after
+- Escape quotes with backslash
+- Use \n for newlines
+- Ensure valid JSON syntax
+- Do NOT return null values
+- Generate COMPLETE, FUNCTIONAL code for ALL files
+
+CREATE PROFESSIONAL, PRODUCTION-READY CODE FOR VISTAPANEL! 🚀"""
+
+ # Enhanced user prompt with detailed specifications
+ features_text = ', '.join(req.features) if req.features else 'Standard features'
+ color_guide = _get_color_scheme_guide(req.color_scheme)
+
+ user_prompt = f"""🎨 PROJECT BRIEF:
+
+📋 WEBSITE TYPE: {req.business_type.upper()}
+
+💬 CLIENT DESCRIPTION:
+{req.prompt}
+
+🎨 DESIGN SPECIFICATIONS:
+- Color Scheme: {req.color_scheme}
+- Style: Modern, professional, visually stunning
+- Layout: Clean, spacious, well-organized
+- Typography: Professional, readable, hierarchical
+
+✨ REQUIRED FEATURES:
+{features_text}
+
+🗄️ DATABASE: {'YES - Include full MySQL schema with sample data' if req.include_database else 'NO - Static website only'}
+
+🎯 SPECIFIC REQUIREMENTS:
+
+1. HERO SECTION:
+ - Eye-catching headline
+ - Compelling subheadline
+ - Clear call-to-action button
+ - Background: gradient or image
+ - Smooth scroll-down indicator
+
+2. NAVIGATION:
+ - Sticky header on scroll
+ - Smooth scroll to sections
+ - Mobile hamburger menu
+ - Active section highlighting
+
+3. CONTENT SECTIONS:
+ - About/Services section with icons
+ - Features/Benefits with cards
+ - Testimonials with star ratings (if requested)
+ - Gallery with lightbox (if requested)
+ - Pricing tables (if requested)
+ - Team members (if requested)
+ - Blog posts grid (if requested)
+ - FAQ accordion (if applicable)
+
+4. CONTACT SECTION:
+ - Working contact form with validation
+ - Email, phone, address display
+ - Social media links
+ - Google Maps embed (placeholder)
+
+5. FOOTER:
+ - Multi-column layout
+ - Quick links
+ - Social media icons
+ - Copyright notice
+ - Back-to-top button
+
+6. INTERACTIVE ELEMENTS:
+ - Smooth scroll animations (fade-in, slide-up)
+ - Hover effects on cards and buttons
+ - Loading states for forms
+ - Success/error messages
+ - Image lazy loading
+ - Parallax effects (subtle)
+
+7. FORMS (if applicable):
+ - Real-time validation
+ - Clear error messages
+ - Success confirmation
+ - Spam protection (honeypot)
+ - Required field indicators
+
+8. PERFORMANCE:
+ - Optimized CSS (no unused styles)
+ - Efficient JavaScript (no jQuery)
+ - Fast loading animations
+ - Minimal HTTP requests
+
+9. SEO:
+ - Proper meta tags (title, description, keywords)
+ - Open Graph tags for social sharing
+ - Structured data (JSON-LD)
+ - Semantic HTML structure
+ - Alt text for all images
+
+10. ACCESSIBILITY:
+ - ARIA labels where needed
+ - Keyboard navigation support
+ - Focus indicators
+ - Sufficient color contrast
+ - Screen reader friendly
+
+{f'''11. DATABASE STRUCTURE (if applicable):
+ - Users table (id, name, email, password, created_at)
+ - Content table (id, title, description, image, created_at)
+ - Messages/Contacts table (id, name, email, message, created_at)
+ - Categories/Tags (if blog)
+ - Proper relationships and indexes
+ - Sample data for testing''' if req.include_database else ''}
+
+🎨 COLOR SCHEME GUIDANCE:
+{color_guide}
+
+📱 RESPONSIVE BREAKPOINTS:
+- Mobile: 320px - 767px (single column, stacked layout)
+- Tablet: 768px - 1023px (2 columns where appropriate)
+- Desktop: 1024px+ (full layout, max-width 1400px)
+
+⚡ MUST INCLUDE:
+- Favicon link (placeholder)
+- Google Fonts (1-2 professional fonts)
+- Font Awesome icons (CDN)
+- Smooth scroll behavior
+- Loading animation on page load
+- 404 error handling
+- Print stylesheet basics
+
+🚫 AVOID:
+- jQuery or heavy frameworks
+- Inline styles (except critical CSS)
+- !important in CSS (unless necessary)
+- Console.log in production code
+- Hardcoded sensitive data
+- Broken links or placeholder text
+
+📦 DELIVERABLES:
+Return ONLY valid JSON with these exact keys:
+- html: Complete HTML5 document
+- css: Complete stylesheet with comments
+- js: Complete JavaScript with error handling
+- sql: MySQL schema with sample data (if database requested)
+- config: PHP database config (if database requested)
+- readme: Detailed setup and customization guide
+
+CREATE A WEBSITE THAT WILL IMPRESS AND DELIGHT! 🚀✨"""
+
+ logger.info(f"Prompt sizes - System: {len(system_prompt)}, User: {len(user_prompt)}")
+
+ # Call Groq API
+ headers = {
+ "Authorization": f"Bearer {api_key}",
+ "Content-Type": "application/json"
+ }
+
+ payload = {
+ "model": model,
+ "messages": [
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": user_prompt}
+ ],
+ "temperature": 0.7,
+ "max_tokens": 8000,
+ "top_p": 0.9
+ }
+
+ logger.info("Calling Groq API...")
+ response = requests.post(
+ "https://api.groq.com/openai/v1/chat/completions",
+ headers=headers,
+ json=payload,
+ timeout=60
+ )
+
+ logger.info(f"Groq Response: {response.status_code}")
+
+ if response.status_code != 200:
+ logger.error(f"Groq Error: {response.text}")
+ raise HTTPException(status_code=response.status_code, detail=response.text)
+
+ result = response.json()
+ raw_content = result['choices'][0]['message']['content']
+ logger.info(f"Response length: {len(raw_content)} chars")
+
+ # Try to extract JSON from markdown code blocks if present
+ if '```json' in raw_content:
+ import re
+ json_match = re.search(r'```json\s*(.+?)\s*```', raw_content, re.DOTALL)
+ if json_match:
+ raw_content = json_match.group(1)
+ logger.info("Extracted JSON from markdown block")
+ elif '```' in raw_content:
+ import re
+ json_match = re.search(r'```\s*(.+?)\s*```', raw_content, re.DOTALL)
+ if json_match:
+ raw_content = json_match.group(1)
+ logger.info("Extracted content from code block")
+
+ logger.info(f"Preview: {raw_content[:200]}...")
+
+ try:
+ generated_content = json.loads(raw_content)
+
+ # Validate that we have the expected structure
+ if not isinstance(generated_content, dict):
+ raise ValueError("Response is not a JSON object")
+
+ # Check if we have the required files
+ required_keys = ['html', 'css', 'js']
+ has_required = any(key in generated_content for key in required_keys)
+
+ if not has_required:
+ # Check if the response is double-encoded (JSON string containing JSON)
+ if len(generated_content) == 1:
+ first_key = list(generated_content.keys())[0]
+ first_value = generated_content[first_key]
+ if isinstance(first_value, str) and first_value.strip().startswith('{'):
+ try:
+ inner_json = json.loads(first_value)
+ if isinstance(inner_json, dict) and any(k in inner_json for k in required_keys):
+ generated_content = inner_json
+ logger.info("Unwrapped double-encoded JSON")
+ except:
+ pass
+
+ # Final validation
+ if 'html' not in generated_content:
+ logger.warning("No HTML in response, using fallback")
+ raise ValueError("Missing HTML content")
+
+ logger.info(f"Files: {list(generated_content.keys())}")
+
+ except (json.JSONDecodeError, ValueError) as je:
+ logger.error(f"JSON parse failed: {str(je)}")
+
+ # Try to fix common JSON issues
+ try:
+ # Remove markdown code blocks if still present
+ if '```' in raw_content:
+ parts = raw_content.split('```')
+ for part in parts:
+ if '{' in part and '}' in part:
+ raw_content = part.replace('json\n', '').replace('json', '').strip()
+ break
+
+ # Try parsing again
+ generated_content = json.loads(raw_content)
+
+ # Validate again
+ if not isinstance(generated_content, dict) or 'html' not in generated_content:
+ raise ValueError("Invalid structure after cleanup")
+
+ logger.info("Fixed JSON after cleanup")
+ logger.info(f"Files: {list(generated_content.keys())}")
+
+ except Exception as cleanup_error:
+ # Final fallback: create minimal structure only if we have HTML-like content
+ logger.warning(f"Using fallback structure: {str(cleanup_error)}")
+
+ if ' str:
+ """Return detailed color guidance based on selected scheme"""
+ schemes = {
+ "modern": """Primary: #6366f1 (Indigo), Secondary: #8b5cf6 (Purple)
+Accent: #ec4899 (Pink), Background: #0f172a (Dark Blue)
+Text: #f1f5f9 (Light Gray), Use gradients and glassmorphism""",
+ "dark": """Primary: #3b82f6 (Blue), Secondary: #10b981 (Green)
+Accent: #f59e0b (Amber), Background: #111827 (Very Dark)
+Text: #f9fafb (White), High contrast, neon accents""",
+ "light": """Primary: #2563eb (Blue), Secondary: #7c3aed (Purple)
+Accent: #dc2626 (Red), Background: #ffffff (White)
+Text: #1f2937 (Dark Gray), Clean, minimal, lots of whitespace""",
+ "vibrant": """Primary: #f43f5e (Rose), Secondary: #8b5cf6 (Purple)
+Accent: #f59e0b (Amber), Background: #1e293b (Dark Slate)
+Text: #f1f5f9 (Light), Bold colors, high energy""",
+ "professional": """Primary: #1e40af (Navy), Secondary: #475569 (Slate)
+Accent: #0891b2 (Cyan), Background: #f8fafc (Off White)
+Text: #0f172a (Dark), Corporate, trustworthy""",
+ "warm": """Primary: #ea580c (Orange), Secondary: #dc2626 (Red)
+Accent: #facc15 (Yellow), Background: #292524 (Warm Dark)
+Text: #fafaf9 (Warm White), Cozy, inviting""",
+ "nature": """Primary: #16a34a (Green), Secondary: #65a30d (Lime)
+Accent: #0891b2 (Teal), Background: #1c1917 (Earth Dark)
+Text: #fafaf9 (Natural White), Organic, earthy"""
+ }
+ return schemes.get(color_scheme, schemes["modern"])
+
+@app.post("/create")
+def create_account(req: AccountRequest):
+ """Create MOFH hosting account"""
+ REQUEST_COUNT["total"] += 1
+ REQUEST_COUNT["create"] += 1
+
+ try:
+ # Use HTTP Basic Authentication (the correct method)
+ auth_string = f"{MOFH_API_USERNAME}:{MOFH_API_PASSWORD}"
+ auth_b64 = base64.b64encode(auth_string.encode('utf-8')).decode('utf-8')
+
+ headers = {
+ 'Authorization': f'Basic {auth_b64}',
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ }
+
+ # Data WITHOUT api_user/api_key (they're in the header)
+ api_data = {
+ 'username': req.username,
+ 'password': req.password,
+ 'contactemail': req.email,
+ 'domain': req.domain,
+ 'plan': req.plan
+ }
+
+ response = requests.post(
+ f"{MOFH_API_URL}createacct.php",
+ data=api_data,
+ headers=headers,
+ timeout=30,
+ verify=False
+ )
+
+ return {
+ 'success': response.status_code == 200,
+ 'http_code': response.status_code,
+ 'response': response.text,
+ 'debug': {
+ 'api_url': f"{MOFH_API_URL}createacct.php",
+ 'auth_method': 'Basic Auth (HTTP Header)',
+ 'api_user_length': len(MOFH_API_USERNAME),
+ 'api_key_length': len(MOFH_API_PASSWORD),
+ 'sent_params': list(api_data.keys())
+ }
+ }
+
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ raise HTTPException(status_code=500, detail=str(e))
+
+class FTPDeployRequest(BaseModel):
+ ftp_host: str
+ ftp_user: str
+ ftp_pass: str
+ files: dict # {"index.html": "content", "style.css": "content", ...}
+
+ @validator('ftp_host')
+ def validate_host(cls, v):
+ if not v or len(v) < 3:
+ raise ValueError('Invalid FTP host')
+ return v.strip()
+
+@app.post("/deploy-ftp")
+def deploy_via_ftp(req: FTPDeployRequest):
+ """Deploy files to FTP server (HF Space has no firewall restrictions)"""
+ REQUEST_COUNT["total"] += 1
+
+ try:
+ import ftplib
+ from io import BytesIO
+
+ logger.info(f"[FTP-DEPLOY] Connecting to {req.ftp_host}")
+
+ deployed_files = []
+ errors = []
+
+ # Try regular FTP first
+ ftp = None
+ try:
+ ftp = ftplib.FTP(timeout=30)
+ ftp.connect(req.ftp_host, 21)
+ ftp.login(req.ftp_user, req.ftp_pass)
+ logger.info("[FTP-DEPLOY] Connected via regular FTP")
+ except Exception as e:
+ logger.warning(f"[FTP-DEPLOY] Regular FTP failed: {e}")
+ # Try FTP_TLS
+ try:
+ ftp = ftplib.FTP_TLS(timeout=30)
+ ftp.connect(req.ftp_host, 21)
+ ftp.login(req.ftp_user, req.ftp_pass)
+ ftp.prot_p() # Enable encryption
+ logger.info("[FTP-DEPLOY] Connected via FTP_TLS")
+ except Exception as e2:
+ logger.error(f"[FTP-DEPLOY] FTP_TLS also failed: {e2}")
+ raise HTTPException(status_code=500, detail=f"FTP connection failed: {str(e2)}")
+
+ # Set passive mode
+ ftp.set_pasv(True)
+
+ # Try to change to web directory
+ web_dirs = ['htdocs', 'public_html', 'www', 'html']
+ current_dir = '/'
+
+ for web_dir in web_dirs:
+ try:
+ ftp.cwd(web_dir)
+ current_dir = ftp.pwd()
+ logger.info(f"[FTP-DEPLOY] Changed to directory: {current_dir}")
+ break
+ except:
+ continue
+
+ logger.info(f"[FTP-DEPLOY] Working directory: {current_dir}")
+
+ # Upload each file
+ for filename, content in req.files.items():
+ try:
+ logger.info(f"[FTP-DEPLOY] Uploading {filename} ({len(content)} bytes)")
+
+ # Convert string to bytes
+ file_bytes = BytesIO(content.encode('utf-8'))
+
+ # Upload file
+ ftp.storbinary(f'STOR {filename}', file_bytes)
+
+ deployed_files.append(filename)
+ logger.info(f"[FTP-DEPLOY] ✓ {filename} uploaded")
+
+ except Exception as e:
+ error_msg = f"{filename}: {str(e)}"
+ errors.append(error_msg)
+ logger.error(f"[FTP-DEPLOY] ✗ {error_msg}")
+
+ # Close FTP connection
+ try:
+ ftp.quit()
+ except:
+ ftp.close()
+
+ logger.info(f"[FTP-DEPLOY] Deployment complete. Success: {len(deployed_files)}, Errors: {len(errors)}")
+
+ if not deployed_files:
+ raise HTTPException(status_code=500, detail=f"No files deployed. Errors: {', '.join(errors)}")
+
+ return {
+ "success": True,
+ "deployed_files": deployed_files,
+ "errors": errors,
+ "message": f"Successfully deployed {len(deployed_files)} file(s)"
+ }
+
+ except HTTPException:
+ raise
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"[FTP-DEPLOY] Error: {str(e)}")
+ raise HTTPException(status_code=500, detail=f"Deployment failed: {str(e)}")
+
+
+class GenerateAndDeployRequest(BaseModel):
+ prompt: str
+ business_type: Optional[str] = "general"
+ color_scheme: Optional[str] = "modern"
+ include_database: Optional[bool] = False
+ features: Optional[List[str]] = []
+ ftp_host: str
+ ftp_user: str
+ ftp_pass: str
+
+ @validator('prompt')
+ def validate_prompt(cls, v):
+ if not v or len(v.strip()) < 10:
+ raise ValueError('Prompt must be at least 10 characters')
+ if len(v) > 10000:
+ raise ValueError('Prompt too long (max 10000 characters)')
+ return v.strip()
+
+@app.post("/generate-and-deploy")
+def generate_and_deploy(req: GenerateAndDeployRequest):
+ """Generate website with AI and deploy to FTP in one call"""
+ REQUEST_COUNT["total"] += 1
+ REQUEST_COUNT["generate"] += 1
+
+ logs = []
+
+ try:
+ import ftplib
+ from io import BytesIO
+
+ logs.append({"time": time.time(), "type": "info", "message": "Starting AI generation..."})
+
+ logger.info("=== IONA AI Generation + Deployment Started ===")
+ logger.info(f"Prompt: {req.prompt[:100]}...")
+ logger.info(f"FTP Host: {req.ftp_host}, User: {req.ftp_user}")
+
+ api_key = get_next_api_key()
+ model = get_random_model()
+
+ logs.append({"time": time.time(), "type": "info", "message": f"Using model: {model}"})
+
+ # Generate with AI - FORCE JSON MODE WITH DETAILED INSTRUCTIONS
+ system_prompt = """You are a web development code generator.
+
+Generate a complete, functional website with HTML, CSS, and JavaScript.
+
+Return a JSON object with these keys:
+- html: A complete HTML5 document with proper structure
+- css: Complete CSS stylesheet with modern styling
+- js: JavaScript code for interactivity
+- readme: Brief setup instructions
+
+The website must be:
+- Fully functional and complete
+- Mobile responsive
+- Modern and professional
+- Include all requested features
+
+IMPORTANT CODE FORMATTING RULES:
+- Use proper indentation (2 spaces per level)
+- Add newlines between sections
+- Format code readably with line breaks
+- Do NOT put everything on one line
+- Use \n for newlines in JSON strings
+- Properly indent nested elements
+
+IMPORTANT: Do NOT return null values. Generate actual code for each field."""
+
+ user_prompt = f"""Generate a {req.business_type} website with this description:
+
+{req.prompt}
+
+Use {req.color_scheme} color scheme and include {', '.join(req.features) if req.features else 'standard'} features.
+
+Return as JSON with html, css, js, and readme keys."""
+
+ headers = {
+ "Authorization": f"Bearer {api_key}",
+ "Content-Type": "application/json"
+ }
+
+ payload = {
+ "model": model,
+ "messages": [
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": user_prompt}
+ ],
+ "temperature": 0.7, # Balanced for creativity and consistency
+ "max_tokens": 32000, # Maximum tokens for complete generation
+ "response_format": {"type": "json_object"} # Force JSON mode
+ }
+
+ logs.append({"time": time.time(), "type": "info", "message": "Calling Groq AI..."})
+
+ response = requests.post(
+ "https://api.groq.com/openai/v1/chat/completions",
+ headers=headers,
+ json=payload,
+ timeout=180 # 3 minutes for complex generation
+ )
+
+ if response.status_code != 200:
+ logs.append({"time": time.time(), "type": "error", "message": f"Groq API error: HTTP {response.status_code}"})
+ raise HTTPException(status_code=response.status_code, detail=response.text)
+
+ result = response.json()
+ raw_content = result['choices'][0]['message']['content']
+
+ logger.info(f"[GENERATE-DEPLOY] Raw response length: {len(raw_content)}")
+ logger.info(f"[GENERATE-DEPLOY] Raw response preview: {raw_content[:200]}")
+
+ logs.append({"time": time.time(), "type": "success", "message": "AI generation complete"})
+ logs.append({"time": time.time(), "type": "info", "message": "Parsing AI response..."})
+
+ # Parse JSON with better error handling
+ generated_content = None
+
+ # Try extracting from markdown code blocks
+ if '```json' in raw_content:
+ import re
+ json_match = re.search(r'```json\s*(.+?)\s*```', raw_content, re.DOTALL)
+ if json_match:
+ raw_content = json_match.group(1)
+ logger.info("[GENERATE-DEPLOY] Extracted from ```json block")
+ elif '```' in raw_content:
+ import re
+ json_match = re.search(r'```\s*(.+?)\s*```', raw_content, re.DOTALL)
+ if json_match:
+ raw_content = json_match.group(1)
+ logger.info("[GENERATE-DEPLOY] Extracted from ``` block")
+
+ # Try parsing JSON
+ try:
+ generated_content = json.loads(raw_content)
+ logger.info(f"[GENERATE-DEPLOY] Parsed JSON successfully, keys: {list(generated_content.keys())}")
+ except json.JSONDecodeError as e:
+ logger.error(f"[GENERATE-DEPLOY] JSON parse failed: {e}")
+ logger.error(f"[GENERATE-DEPLOY] Content: {raw_content[:500]}")
+
+ # Try to find JSON object in the response
+ import re
+ json_pattern = r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}'
+ matches = re.findall(json_pattern, raw_content, re.DOTALL)
+
+ for match in matches:
+ try:
+ generated_content = json.loads(match)
+ if 'html' in generated_content:
+ logger.info("[GENERATE-DEPLOY] Found valid JSON in response")
+ break
+ except:
+ continue
+
+ if not generated_content:
+ logs.append({"time": time.time(), "type": "error", "message": "AI returned invalid JSON format"})
+ raise ValueError(f"AI response is not valid JSON. Error: {str(e)}. Response preview: {raw_content[:200]}")
+
+ if 'html' not in generated_content:
+ logs.append({"time": time.time(), "type": "error", "message": "No HTML in AI response"})
+ raise ValueError("No HTML in AI response")
+
+ # Check if HTML is null or empty
+ html_content = generated_content.get('html', '')
+ if not html_content or html_content == 'null' or len(html_content.strip()) < 100:
+ logs.append({"time": time.time(), "type": "error", "message": f"AI returned invalid HTML (length: {len(html_content)})"})
+ logs.append({"time": time.time(), "type": "error", "message": f"Model used: {model}"})
+ logs.append({"time": time.time(), "type": "error", "message": f"Response preview: {str(generated_content)[:200]}"})
+ raise ValueError(f"AI returned null or empty HTML content. Model: {model}. Please try again.")
+
+ logs.append({"time": time.time(), "type": "success", "message": "AI response parsed successfully"})
+ logs.append({"time": time.time(), "type": "info", "message": f"Generated {len(generated_content.get('html', ''))} chars of HTML"})
+
+ logs.append({"time": time.time(), "type": "info", "message": "FTP blocked - returning files to PHP for deployment"})
+
+ # HF Space cannot connect to FTP (port 21 blocked)
+ # Return generated files to PHP for deployment
+ logger.info("[GENERATE-DEPLOY] FTP blocked by HF network - returning files")
+
+ return {
+ "success": True,
+ "model_used": model,
+ "deployed_files": [],
+ "files": {
+ 'html': generated_content.get('html', ''),
+ 'css': generated_content.get('css', ''),
+ 'js': generated_content.get('js', ''),
+ 'php': generated_content.get('php', ''),
+ 'sql': generated_content.get('sql', ''),
+ 'config': generated_content.get('config', ''),
+ 'readme': generated_content.get('readme', '')
+ },
+ "ftp_blocked": True,
+ "logs": logs,
+ "message": "Website generated successfully. FTP blocked - files returned for PHP deployment."
+ }
+
+ except HTTPException:
+ raise
+ except json.JSONDecodeError as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"[GENERATE-DEPLOY] JSON Error: {str(e)}")
+ logs.append({"time": time.time(), "type": "error", "message": f"JSON parsing failed: {str(e)}"})
+ raise HTTPException(status_code=500, detail=f"AI returned invalid JSON format. Please try again. Error: {str(e)}")
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"[GENERATE-DEPLOY] Error: {str(e)}")
+ logs.append({"time": time.time(), "type": "error", "message": str(e)})
+ raise HTTPException(status_code=500, detail=f"Generation/Deployment failed: {str(e)}")
+
+
+# ============================================================================
+# GITHUB DEPLOYMENT ENDPOINTS
+# ============================================================================
+
+from github_deploy import GitHubDeployment
+from pydantic import BaseModel
+
+# Initialize GitHub Deployment (will be configured via environment variables)
+GITHUB_APP_ID = os.getenv('GITHUB_APP_ID', '')
+GITHUB_CLIENT_ID = os.getenv('GITHUB_CLIENT_ID', '')
+GITHUB_CLIENT_SECRET = os.getenv('GITHUB_CLIENT_SECRET', '')
+GITHUB_PRIVATE_KEY = os.getenv('GITHUB_PRIVATE_KEY', '').replace('\\n', '\n')
+
+github_deploy = None
+if GITHUB_APP_ID and GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET and GITHUB_PRIVATE_KEY:
+ try:
+ github_deploy = GitHubDeployment(
+ GITHUB_APP_ID,
+ GITHUB_CLIENT_ID,
+ GITHUB_CLIENT_SECRET,
+ GITHUB_PRIVATE_KEY
+ )
+ logger.info("✅ GitHub Deployment initialized")
+ except Exception as e:
+ logger.error(f"❌ GitHub Deployment initialization failed: {e}")
+else:
+ logger.warning("⚠️ GitHub Deployment not configured (missing environment variables)")
+
+
+class GitHubOAuthRequest(BaseModel):
+ code: str
+
+
+class GitHubRepoRequest(BaseModel):
+ access_token: str
+
+
+class GitHubSetupDeploymentRequest(BaseModel):
+ owner: str
+ repo: str
+ access_token: str
+ ftp_server: str
+ ftp_username: str
+ ftp_password: str
+ branch: Optional[str] = 'main'
+
+
+class GitHubWorkflowRunsRequest(BaseModel):
+ owner: str
+ repo: str
+ access_token: str
+ limit: Optional[int] = 10
+
+
+class GitHubWorkflowLogsRequest(BaseModel):
+ owner: str
+ repo: str
+ run_id: int
+ access_token: str
+
+
+class GitHubTriggerWorkflowRequest(BaseModel):
+ owner: str
+ repo: str
+ access_token: str
+ workflow_file: Optional[str] = 'celestine_deploy.yml'
+ branch: Optional[str] = 'main'
+
+
+@app.get("/github/status")
+def github_status():
+ """Check if GitHub integration is configured"""
+ return {
+ "configured": github_deploy is not None,
+ "app_id": GITHUB_APP_ID if GITHUB_APP_ID else None,
+ "client_id": GITHUB_CLIENT_ID if GITHUB_CLIENT_ID else None,
+ "message": "GitHub integration is ready" if github_deploy else "GitHub integration not configured"
+ }
+
+
+@app.post("/github/oauth/exchange")
+def github_oauth_exchange(req: GitHubOAuthRequest):
+ """Exchange OAuth code for access token"""
+ REQUEST_COUNT["total"] += 1
+
+ if not github_deploy:
+ raise HTTPException(status_code=503, detail="GitHub integration not configured")
+
+ try:
+ result = github_deploy.exchange_code_for_token(req.code)
+ return {
+ "success": True,
+ "access_token": result.get('access_token'),
+ "token_type": result.get('token_type'),
+ "scope": result.get('scope')
+ }
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"OAuth exchange failed: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+@app.post("/github/repos")
+def github_get_repos(req: GitHubRepoRequest):
+ """Get user's GitHub repositories"""
+ REQUEST_COUNT["total"] += 1
+
+ if not github_deploy:
+ raise HTTPException(status_code=503, detail="GitHub integration not configured")
+
+ try:
+ repos = github_deploy.get_user_repos(req.access_token)
+ return {
+ "success": True,
+ "repositories": repos,
+ "count": len(repos)
+ }
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"Failed to get repos: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+@app.post("/github/detect-project")
+def github_detect_project(req: GitHubSetupDeploymentRequest):
+ """Detect project type and build configuration"""
+ REQUEST_COUNT["total"] += 1
+
+ if not github_deploy:
+ raise HTTPException(status_code=503, detail="GitHub integration not configured")
+
+ try:
+ project_config = github_deploy.detect_project_type(
+ req.owner,
+ req.repo,
+ req.access_token
+ )
+ return {
+ "success": True,
+ "project_config": project_config
+ }
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"Project detection failed: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+@app.post("/github/setup-deployment")
+def github_setup_deployment(req: GitHubSetupDeploymentRequest):
+ """
+ Complete deployment setup:
+ 1. Detect project type
+ 2. Create FTP secrets
+ 3. Inject workflow file
+ """
+ REQUEST_COUNT["total"] += 1
+
+ if not github_deploy:
+ raise HTTPException(status_code=503, detail="GitHub integration not configured")
+
+ try:
+ logger.info(f"Setting up deployment for {req.owner}/{req.repo}")
+
+ result = github_deploy.setup_deployment(
+ req.owner,
+ req.repo,
+ req.access_token,
+ req.ftp_server,
+ req.ftp_username,
+ req.ftp_password,
+ req.branch
+ )
+
+ if not result['success']:
+ raise HTTPException(status_code=500, detail=result.get('error', 'Setup failed'))
+
+ return result
+ except HTTPException:
+ raise
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"Deployment setup failed: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+@app.post("/github/workflow-runs")
+def github_workflow_runs(req: GitHubWorkflowRunsRequest):
+ """Get workflow run history"""
+ REQUEST_COUNT["total"] += 1
+
+ if not github_deploy:
+ raise HTTPException(status_code=503, detail="GitHub integration not configured")
+
+ try:
+ runs = github_deploy.get_workflow_runs(
+ req.owner,
+ req.repo,
+ req.access_token,
+ req.limit
+ )
+ return {
+ "success": True,
+ "runs": runs,
+ "count": len(runs)
+ }
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"Failed to get workflow runs: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+@app.post("/github/workflow-logs")
+def github_workflow_logs(req: GitHubWorkflowLogsRequest):
+ """Get workflow run logs"""
+ REQUEST_COUNT["total"] += 1
+
+ if not github_deploy:
+ raise HTTPException(status_code=503, detail="GitHub integration not configured")
+
+ try:
+ logs = github_deploy.get_workflow_logs(
+ req.owner,
+ req.repo,
+ req.run_id,
+ req.access_token
+ )
+ return {
+ "success": True,
+ "logs": logs
+ }
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"Failed to get logs: {e}")
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+@app.post("/github/trigger-workflow")
+def github_trigger_workflow(req: GitHubTriggerWorkflowRequest):
+ """Manually trigger a workflow"""
+ REQUEST_COUNT["total"] += 1
+
+ if not github_deploy:
+ raise HTTPException(status_code=503, detail="GitHub integration not configured")
+
+ try:
+ success = github_deploy.trigger_workflow(
+ req.owner,
+ req.repo,
+ req.workflow_file,
+ req.access_token,
+ req.branch
+ )
+
+ if not success:
+ raise HTTPException(status_code=500, detail="Failed to trigger workflow")
+
+ return {
+ "success": True,
+ "message": "Workflow triggered successfully"
+ }
+ except HTTPException:
+ raise
+ except Exception as e:
+ REQUEST_COUNT["errors"] += 1
+ logger.error(f"Failed to trigger workflow: {e}")
+ raise HTTPException(status_code=500, detail=str(e))