Spaces:
Build error
Build error
| import os | |
| import json | |
| import zipfile | |
| import shutil | |
| import subprocess | |
| import tempfile | |
| from pathlib import Path | |
| from typing import Dict, List, Optional | |
| import threading | |
| import time | |
| from http.server import HTTPServer, SimpleHTTPRequestHandler | |
| import socket | |
| class FileHandler: | |
| """Handle file operations for website projects""" | |
| def __init__(self): | |
| self.projects_dir = Path("projects") | |
| self.projects_dir.mkdir(exist_ok=True) | |
| def create_project(self, project_name: str, files: Dict[str, str]) -> Path: | |
| """Create a new project with generated files""" | |
| project_path = self.projects_dir / project_name | |
| # Remove existing project if it exists | |
| if project_path.exists(): | |
| shutil.rmtree(project_path) | |
| project_path.mkdir(exist_ok=True) | |
| # Write all files | |
| for file_path, content in files.items(): | |
| full_path = project_path / file_path | |
| full_path.parent.mkdir(parents=True, exist_ok=True) | |
| with open(full_path, 'w', encoding='utf-8') as f: | |
| f.write(content) | |
| return project_path | |
| def get_project_files(self, project_path: Path) -> Dict[str, str]: | |
| """Get all files in a project""" | |
| files = {} | |
| if project_path.exists(): | |
| for file_path in project_path.rglob("*"): | |
| if file_path.is_file(): | |
| relative_path = file_path.relative_to(project_path) | |
| files[str(relative_path)] = self.get_file_content(project_path, str(relative_path)) | |
| return files | |
| def get_file_content(self, project_path: Path, filename: str) -> str: | |
| """Get content of a specific file""" | |
| file_path = project_path / filename | |
| if file_path.exists(): | |
| with open(file_path, 'r', encoding='utf-8') as f: | |
| return f.read() | |
| return "" | |
| def create_zip(self, project_path: Path) -> str: | |
| """Create a ZIP file of the project""" | |
| zip_path = f"{project_path}.zip" | |
| with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: | |
| for file_path in project_path.rglob("*"): | |
| if file_path.is_file(): | |
| arcname = file_path.relative_to(project_path.parent) | |
| zipf.write(file_path, arcname) | |
| return zip_path | |
| class PreviewManager: | |
| """Manage live preview of websites""" | |
| def __init__(self): | |
| self.active_servers = {} | |
| self.port_counter = 8000 | |
| def get_free_port(self) -> int: | |
| """Find a free port for the preview server""" | |
| while True: | |
| try: | |
| with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: | |
| s.bind(('localhost', self.port_counter)) | |
| return self.port_counter | |
| except OSError: | |
| self.port_counter += 1 | |
| def create_preview(self, project_path: Path) -> str: | |
| """Create a live preview server for the project""" | |
| port = self.get_free_port() | |
| # Start server in a separate thread | |
| server_thread = threading.Thread( | |
| target=self._run_server, | |
| args=(project_path, port), | |
| daemon=True | |
| ) | |
| server_thread.start() | |
| # Give server time to start | |
| time.sleep(0.5) | |
| self.active_servers[str(project_path)] = port | |
| return f"http://localhost:{port}" | |
| def update_preview(self, project_path: Path) -> str: | |
| """Update the preview (server already running)""" | |
| if str(project_path) in self.active_servers: | |
| port = self.active_servers[str(project_path)] | |
| return f"http://localhost:{port}" | |
| return self.create_preview(project_path) | |
| def _run_server(self, project_path: Path, port: int): | |
| """Run HTTP server for preview""" | |
| os.chdir(project_path) | |
| class CustomHandler(SimpleHTTPRequestHandler): | |
| def __init__(self, *args, **kwargs): | |
| super().__init__(*args, directory=project_path, **kwargs) | |
| def end_headers(self): | |
| # Add CORS headers | |
| self.send_header('Access-Control-Allow-Origin', '*') | |
| self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') | |
| self.send_header('Access-Control-Allow-Headers', 'Content-Type') | |
| super().end_headers() | |
| try: | |
| httpd = HTTPServer(('localhost', port), CustomHandler) | |
| httpd.serve_forever() | |
| except Exception as e: | |
| print(f"Server error: {e}") | |
| def detect_file_language(filename: str) -> str: | |
| """Detect programming language from file extension""" | |
| ext = Path(filename).suffix.lower() | |
| lang_map = { | |
| '.html': 'html', | |
| '.css': 'css', | |
| '.js': 'javascript', | |
| '.json': 'json', | |
| '.md': 'markdown', | |
| '.py': 'python', | |
| '.jsx': 'jsx', | |
| '.tsx': 'typescript', | |
| '.ts': 'typescript', | |
| '.xml': 'xml', | |
| '.yaml': 'yaml', | |
| '.yml': 'yaml', | |
| '.sql': 'sql', | |
| '.php': 'php', | |
| '.rb': 'ruby', | |
| '.go': 'go', | |
| '.java': 'java', | |
| '.c': 'c', | |
| '.cpp': 'cpp', | |
| '.h': 'c', | |
| '.hpp': 'cpp', | |
| '.scss': 'scss', | |
| '.sass': 'sass', | |
| '.less': 'less', | |
| '.vue': 'vue', | |
| '.svelte': 'svelte' | |
| } | |
| return lang_map.get(ext, 'text') | |
| def format_file_size(size_bytes: int) -> str: | |
| """Format file size in human readable format""" | |
| if size_bytes == 0: | |
| return "0B" | |
| size_names = ["B", "KB", "MB", "GB"] | |
| i = 0 | |
| while size_bytes >= 1024 and i < len(size_names) - 1: | |
| size_bytes /= 1024.0 | |
| i += 1 | |
| return f"{size_bytes:.1f}{size_names[i]}" |