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 - - - -
-
-

Celestine Hosting Backend

-

Intelligent Website Generator & MOFH API Proxy

-
🟢 ONLINE
-
- -
-
-

⏱️ 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}% -
-
-
{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])} -
-
- -
-

📡 API Endpoints

-
    -
  • - GET - / -
    System status dashboard
    -
  • -
  • - POST - /generate-website -
    Generate website with IONA AI
    -
  • -
  • - POST - /create -
    Create MOFH hosting account
    -
  • -
  • - GET - /health -
    Health check endpoint
    -
  • -
  • - GET - /test -
    Test AI generation
    -
  • -
-
- -
-

👨‍💻 Developer Info

-
- Developer - Pratyush Srivastava -
-
- GitHub - @pratyush -
-
- Portfolio - pratyush.dev -
-
- 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
\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 + + + +
+
+

Celestine Hosting Backend

+

Intelligent Website Generator & MOFH API Proxy

+
🟢 ONLINE
+
+ +
+
+

⏱️ 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}% +
+
+
{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])} +
+
+ +
+

📡 API Endpoints

+
    +
  • + GET + / +
    System status dashboard
    +
  • +
  • + POST + /generate-website +
    Generate website with IONA AI
    +
  • +
  • + POST + /create +
    Create MOFH hosting account
    +
  • +
  • + GET + /health +
    Health check endpoint
    +
  • +
  • + GET + /test +
    Test AI generation
    +
  • +
+
+ +
+

👨‍💻 Developer Info

+
+ Developer + Pratyush Srivastava +
+
+ GitHub + @pratyush +
+
+ Portfolio + pratyush.dev +
+
+ 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
\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))