import os import shutil import subprocess import uuid import gradio as gr from fastapi.staticfiles import StaticFiles from fastapi import FastAPI # --- Static Folder Setup --- PREVIEW_PATH = os.path.abspath("hosted_sites") if not os.path.exists(PREVIEW_PATH): os.makedirs(PREVIEW_PATH) def find_index_html(start_dir): """ Project mein index.html dhoondhne ke liye helper function. Build directories ko priority milti hai. """ # Priority folders first for root, dirs, files in os.walk(start_dir): if "index.html" in files: parts = os.path.normpath(root).split(os.sep) if any(p in parts for p in ['dist', 'build', 'out', 'public']): return os.path.relpath(os.path.join(root, "index.html"), PREVIEW_PATH) # Fallback to any index.html for root, dirs, files in os.walk(start_dir): if "index.html" in files: return os.path.relpath(os.path.join(root, "index.html"), PREVIEW_PATH) return None def run_command(cmd, cwd): """Real-time build logs capture karne ke liye generator function""" process = subprocess.Popen( cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True ) while True: output = process.stdout.readline() if output == '' and process.poll() is not None: break if output: yield output.strip() rc = process.poll() if rc != 0: raise subprocess.CalledProcessError(rc, cmd) def deploy_project(zip_file): if zip_file is None: yield "ā Please upload a ZIP file.", "", gr.update(visible=False), gr.update(visible=False) return # Unique deployment folder create karna deploy_id = f"site-{str(uuid.uuid4())[:8]}" deploy_dir = os.path.join(PREVIEW_PATH, deploy_id) os.makedirs(deploy_dir) log_accumulator = [] def log(msg): log_accumulator.append(msg) return "\n".join(log_accumulator) try: # Extract files yield log("š¦ Extracting ZIP archive..."), "", gr.update(visible=False), gr.update(visible=False) shutil.unpack_archive(zip_file.name, deploy_dir) root_path = deploy_dir is_node_project = False # Check node project for root, dirs, files in os.walk(deploy_dir): if "package.json" in files: root_path = root is_node_project = True break # Build steps if is_node_project: yield log("š Node.js project detected."), "", gr.update(visible=False), gr.update(visible=False) yield log("ā” Running 'npm install'..."), "", gr.update(visible=False), gr.update(visible=False) for line in run_command("npm install", root_path): yield log(f"[npm] {line}"), "", gr.update(visible=False), gr.update(visible=False) yield log("šļø Running 'npm run build'..."), "", gr.update(visible=False), gr.update(visible=False) for line in run_command("npm run build", root_path): yield log(f"[build] {line}"), "", gr.update(visible=False), gr.update(visible=False) else: yield log("š Static HTML project detected. Skipping build steps."), "", gr.update(visible=False), gr.update(visible=False) # Locate preview entry-point relative_index = find_index_html(deploy_dir) if relative_index: preview_url = f"/preview/{relative_index.replace(os.sep, '/')}" success_msg = f"š **Deployment Successful!**\n\nYour site is live at: `/preview/{relative_index}`" yield log("š Site deployed successfully!"), success_msg, gr.update(src=preview_url, visible=True), gr.update(value=f"Open App in New Tab š", link=preview_url, visible=True) else: yield log("ā Error: 'index.html' not found."), "ā Error: index.html not found.", gr.update(visible=False), gr.update(visible=False) except subprocess.CalledProcessError as e: yield log(f"\nā Build Failed with exit code {e.returncode}"), "ā Deployment Failed. Check console logs.", gr.update(visible=False), gr.update(visible=False) except Exception as e: yield log(f"\nā Unexpected Error: {str(e)}"), "ā Deployment Failed.", gr.update(visible=False), gr.update(visible=False) # --- UI Setup --- # Removed 'css' parameter to resolve warning in Gradio 6.0 with gr.Blocks(title="Vercel Minimal Clone") as demo: gr.HTML("""
Instant Serverless Hosting & Preview for ZIP Deployments