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

ā–² VERCEL CLONE

Instant Serverless Hosting & Preview for ZIP Deployments

""") with gr.Row(): with gr.Column(scale=1): gr.Markdown("### šŸ› ļø New Deployment") zip_input = gr.File(label="Upload Project ZIP", file_types=[".zip"]) deploy_btn = gr.Button("Deploy to Production", variant="primary") gr.Markdown("### šŸ“‹ Build Logs (Console)") # Fixed: Removed 'language="bash"' and used default rendering console_output = gr.Code(label="Terminal", value="Terminal ready...", lines=12) with gr.Column(scale=1): gr.Markdown("### 🌐 Live Production URL") status_text = gr.Markdown("No active deployment.") external_link = gr.Button("Open App in New Tab 🌐", visible=False) preview_iframe = gr.Iframe( label="Live Frame", src="", width="100%", height="550px", visible=False ) deploy_btn.click( fn=deploy_project, inputs=zip_input, outputs=[console_output, status_text, preview_iframe, external_link] ) # --- FastAPI Server --- app = FastAPI() # Serving static preview folders app.mount("/preview", StaticFiles(directory=PREVIEW_PATH), name="preview") # Mounting Gradio demo_app = gr.mount_gradio_app(app, demo, path="/") if __name__ == "__main__": import uvicorn uvicorn.run(demo_app, host="0.0.0.0", port=7860)